lib/bsd/sys/admin.rb in sys-admin-1.7.0 vs lib/bsd/sys/admin.rb in sys-admin-1.7.1

- old
+ new

@@ -1,256 +1,256 @@ -require 'sys/admin/custom' -require 'sys/admin/common' -require 'rbconfig' - -# The BSD specific code. - -module Sys - class Admin - private - - # :no-doc: - BUF_MAX = 65536 # Max buffer for retry - - # I'm making some aliases here to prevent potential conflicts - attach_function :open_c, :open, [:string, :int], :int - attach_function :pread_c, :pread, [:int, :pointer, :size_t, :off_t], :size_t - attach_function :close_c, :close, [:int], :int - - attach_function :getlogin_r, [:pointer, :int], :int - attach_function :getpwnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int - attach_function :getpwuid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int - attach_function :getgrnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int - attach_function :getgrgid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int - - private_class_method :getlogin_r, :getpwnam_r, :getpwuid_r, :getgrnam_r - private_class_method :getgrgid_r - private_class_method :open_c, :pread_c, :close_c - - # struct passwd from /usr/include/pwd.h - class PasswdStruct < FFI::Struct - fields = [ - :pw_name, :string, - :pw_passwd, :string, - :pw_uid, :uid_t, - :pw_gid, :gid_t, - :pw_change, :time_t, - :pw_class, :string, - :pw_gecos, :string, - :pw_dir, :string, - :pw_shell, :string, - :pw_expire, :time_t - ] - - if RbConfig::CONFIG['host_os'] =~ /freebsd/i - fields.push(:pw_fields, :int) - end - - layout(*fields) - end - - # struct group from /usr/include/grp.h - class GroupStruct < FFI::Struct - layout( - :gr_name, :string, - :gr_passwd, :string, - :gr_gid, :uint, - :gr_mem, :pointer - ) - end - - # I'm blending the timeval struct in directly here - class LastlogStruct < FFI::Struct - layout( - :ll_time, :int32, - :ll_line, [:char, 32], - :ll_host, [:char, 256] - ) - end - - public - - # Returns the login for the current process. - # - def self.get_login - buf = FFI::MemoryPointer.new(:char, 256) - - if getlogin_r(buf, buf.size) != 0 - raise Error, "getlogin_r function failed: " + strerror(FFI.errno) - end - - buf.read_string - end - - # Returns a User object for the given name or uid. Raises an error - # if a user cannot be found. - # - # Examples: - # - # Sys::Admin.get_user('joe') - # Sys::Admin.get_user(501) - # - def self.get_user(uid) - buf = FFI::MemoryPointer.new(:char, 1024) - pbuf = FFI::MemoryPointer.new(PasswdStruct) - temp = PasswdStruct.new - - if uid.is_a?(String) - if getpwnam_r(uid, temp, buf, buf.size, pbuf) != 0 - raise Error, "getpwnam_r function failed: " + strerror(FFI.errno) - end - else - if getpwuid_r(uid, temp, buf, buf.size, pbuf) != 0 - raise Error, "getpwuid_r function failed: " + strerror(FFI.errno) - end - end - - ptr = pbuf.read_pointer - - if ptr.null? - raise Error, "no user found for #{uid}" - end - - pwd = PasswdStruct.new(ptr) - get_user_from_struct(pwd) - end - - # Returns a Group object for the given name or uid. Raises an error - # if a group cannot be found. - # - # Examples: - # - # Sys::Admin.get_group('admin') - # Sys::Admin.get_group(101) - # - def self.get_group(gid) - size = 1024 - buf = FFI::MemoryPointer.new(:char, size) - pbuf = FFI::MemoryPointer.new(PasswdStruct) - temp = GroupStruct.new - - begin - if gid.is_a?(String) - val = getgrnam_r(gid, temp, buf, buf.size, pbuf) - fun = 'getgrnam_r' - else - val = getgrgid_r(gid, temp, buf, buf.size, pbuf) - fun = 'getgrgid_r' - end - raise SystemCallError.new(fun, val) if val != 0 - rescue Errno::ERANGE - size += 1024 - raise if size > BUF_MAX - buf = FFI::MemoryPointer.new(:char, size) - retry - end - - ptr = pbuf.read_pointer - - if ptr.null? - raise Error, "no group found for '#{gid}'" - end - - grp = GroupStruct.new(ptr) - get_group_from_struct(grp) - end - - # Returns an array of User objects for each user on the system. - # - def self.users - users = [] - - begin - setpwent() - - until (ptr = getpwent()).null? - pwd = PasswdStruct.new(ptr) - users << get_user_from_struct(pwd) - end - ensure - endpwent() - end - - users - end - - # Returns an array of Group objects for each user on the system. - # - def self.groups - groups = [] - - begin - setgrent() - - until (ptr = getgrent()).null? - grp = GroupStruct.new(ptr) - groups << get_group_from_struct(grp) - end - ensure - endgrent() - end - - groups - end - - private - - # Takes a GroupStruct and converts it to a Group object. - def self.get_group_from_struct(grp) - Group.new do |g| - g.name = grp[:gr_name] - g.passwd = grp[:gr_passwd] - g.gid = grp[:gr_gid] - g.members = grp[:gr_mem].read_array_of_string - end - end - - # Takes a UserStruct and converts it to a User object. - def self.get_user_from_struct(pwd) - user = User.new do |u| - u.name = pwd[:pw_name] - u.passwd = pwd[:pw_passwd] - u.uid = pwd[:pw_uid] - u.gid = pwd[:pw_gid] - u.change = Time.at(pwd[:pw_change]) - u.access_class = pwd[:pw_class] - u.gecos = pwd[:pw_gecos] - u.dir = pwd[:pw_dir] - u.shell = pwd[:pw_shell] - u.expire = Time.at(pwd[:pw_expire]) - end - - log = get_lastlog_info(user.uid) - - if log - user.login_time = Time.at(log[:ll_time]) - user.login_device = log[:ll_line].to_s - user.login_host = log[:ll_host].to_s - end - - user - end - - # Get lastlog information for the given user. - def self.get_lastlog_info(uid) - logfile = '/var/log/lastlog' - lastlog = LastlogStruct.new - - begin - fd = open_c(logfile, File::RDONLY) - - if fd != -1 - bytes = pread_c(fd, lastlog, lastlog.size, uid * lastlog.size) - if bytes < 0 - raise Error, "pread function failed: " + strerror(FFI.errno) - end - else - nil # Ignore, improper permissions - end - ensure - close_c(fd) if fd && fd >= 0 - end - - lastlog - end - end -end +require 'sys/admin/custom' +require 'sys/admin/common' +require 'rbconfig' + +# The BSD specific code. + +module Sys + class Admin + private + + # :no-doc: + BUF_MAX = 65536 # Max buffer for retry + + # I'm making some aliases here to prevent potential conflicts + attach_function :open_c, :open, [:string, :int], :int + attach_function :pread_c, :pread, [:int, :pointer, :size_t, :off_t], :size_t + attach_function :close_c, :close, [:int], :int + + attach_function :getlogin_r, [:pointer, :int], :int + attach_function :getpwnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int + attach_function :getpwuid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int + attach_function :getgrnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int + attach_function :getgrgid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int + + private_class_method :getlogin_r, :getpwnam_r, :getpwuid_r, :getgrnam_r + private_class_method :getgrgid_r + private_class_method :open_c, :pread_c, :close_c + + # struct passwd from /usr/include/pwd.h + class PasswdStruct < FFI::Struct + fields = [ + :pw_name, :string, + :pw_passwd, :string, + :pw_uid, :uid_t, + :pw_gid, :gid_t, + :pw_change, :time_t, + :pw_class, :string, + :pw_gecos, :string, + :pw_dir, :string, + :pw_shell, :string, + :pw_expire, :time_t + ] + + if RbConfig::CONFIG['host_os'] =~ /freebsd/i + fields.push(:pw_fields, :int) + end + + layout(*fields) + end + + # struct group from /usr/include/grp.h + class GroupStruct < FFI::Struct + layout( + :gr_name, :string, + :gr_passwd, :string, + :gr_gid, :uint, + :gr_mem, :pointer + ) + end + + # I'm blending the timeval struct in directly here + class LastlogStruct < FFI::Struct + layout( + :ll_time, :int32, + :ll_line, [:char, 32], + :ll_host, [:char, 256] + ) + end + + public + + # Returns the login for the current process. + # + def self.get_login + buf = FFI::MemoryPointer.new(:char, 256) + + if getlogin_r(buf, buf.size) != 0 + raise Error, "getlogin_r function failed: " + strerror(FFI.errno) + end + + buf.read_string + end + + # Returns a User object for the given name or uid. Raises an error + # if a user cannot be found. + # + # Examples: + # + # Sys::Admin.get_user('joe') + # Sys::Admin.get_user(501) + # + def self.get_user(uid) + buf = FFI::MemoryPointer.new(:char, 1024) + pbuf = FFI::MemoryPointer.new(PasswdStruct) + temp = PasswdStruct.new + + if uid.is_a?(String) + if getpwnam_r(uid, temp, buf, buf.size, pbuf) != 0 + raise Error, "getpwnam_r function failed: " + strerror(FFI.errno) + end + else + if getpwuid_r(uid, temp, buf, buf.size, pbuf) != 0 + raise Error, "getpwuid_r function failed: " + strerror(FFI.errno) + end + end + + ptr = pbuf.read_pointer + + if ptr.null? + raise Error, "no user found for #{uid}" + end + + pwd = PasswdStruct.new(ptr) + get_user_from_struct(pwd) + end + + # Returns a Group object for the given name or uid. Raises an error + # if a group cannot be found. + # + # Examples: + # + # Sys::Admin.get_group('admin') + # Sys::Admin.get_group(101) + # + def self.get_group(gid) + size = 1024 + buf = FFI::MemoryPointer.new(:char, size) + pbuf = FFI::MemoryPointer.new(PasswdStruct) + temp = GroupStruct.new + + begin + if gid.is_a?(String) + val = getgrnam_r(gid, temp, buf, buf.size, pbuf) + fun = 'getgrnam_r' + else + val = getgrgid_r(gid, temp, buf, buf.size, pbuf) + fun = 'getgrgid_r' + end + raise SystemCallError.new(fun, val) if val != 0 + rescue Errno::ERANGE + size += 1024 + raise if size > BUF_MAX + buf = FFI::MemoryPointer.new(:char, size) + retry + end + + ptr = pbuf.read_pointer + + if ptr.null? + raise Error, "no group found for '#{gid}'" + end + + grp = GroupStruct.new(ptr) + get_group_from_struct(grp) + end + + # Returns an array of User objects for each user on the system. + # + def self.users + users = [] + + begin + setpwent() + + until (ptr = getpwent()).null? + pwd = PasswdStruct.new(ptr) + users << get_user_from_struct(pwd) + end + ensure + endpwent() + end + + users + end + + # Returns an array of Group objects for each user on the system. + # + def self.groups + groups = [] + + begin + setgrent() + + until (ptr = getgrent()).null? + grp = GroupStruct.new(ptr) + groups << get_group_from_struct(grp) + end + ensure + endgrent() + end + + groups + end + + private + + # Takes a GroupStruct and converts it to a Group object. + def self.get_group_from_struct(grp) + Group.new do |g| + g.name = grp[:gr_name] + g.passwd = grp[:gr_passwd] + g.gid = grp[:gr_gid] + g.members = grp[:gr_mem].read_array_of_string + end + end + + # Takes a UserStruct and converts it to a User object. + def self.get_user_from_struct(pwd) + user = User.new do |u| + u.name = pwd[:pw_name] + u.passwd = pwd[:pw_passwd] + u.uid = pwd[:pw_uid] + u.gid = pwd[:pw_gid] + u.change = Time.at(pwd[:pw_change]) + u.access_class = pwd[:pw_class] + u.gecos = pwd[:pw_gecos] + u.dir = pwd[:pw_dir] + u.shell = pwd[:pw_shell] + u.expire = Time.at(pwd[:pw_expire]) + end + + log = get_lastlog_info(user.uid) + + if log + user.login_time = Time.at(log[:ll_time]) + user.login_device = log[:ll_line].to_s + user.login_host = log[:ll_host].to_s + end + + user + end + + # Get lastlog information for the given user. + def self.get_lastlog_info(uid) + logfile = '/var/log/lastlog' + lastlog = LastlogStruct.new + + begin + fd = open_c(logfile, File::RDONLY) + + if fd != -1 + bytes = pread_c(fd, lastlog, lastlog.size, uid * lastlog.size) + if bytes < 0 + raise Error, "pread function failed: " + strerror(FFI.errno) + end + else + nil # Ignore, improper permissions + end + ensure + close_c(fd) if fd && fd >= 0 + end + + lastlog + end + end +end