sys-admin 1.8.0-universal-mingw32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/CHANGES.md +204 -0
- data/Gemfile +2 -0
- data/LICENSE +177 -0
- data/MANIFEST.md +20 -0
- data/README.md +162 -0
- data/Rakefile +44 -0
- data/certs/djberg96_pub.pem +26 -0
- data/doc/sys-admin-unix.txt +162 -0
- data/doc/sys-admin-windows.txt +346 -0
- data/examples/example_groups.rb +27 -0
- data/examples/example_users.rb +41 -0
- data/lib/bsd/sys/admin.rb +262 -0
- data/lib/darwin/sys/admin.rb +244 -0
- data/lib/linux/sys/admin.rb +272 -0
- data/lib/sunos/sys/admin.rb +268 -0
- data/lib/sys/admin/common.rb +134 -0
- data/lib/sys/admin/custom.rb +16 -0
- data/lib/sys/admin.rb +24 -0
- data/lib/sys-admin.rb +1 -0
- data/lib/unix/sys/admin.rb +165 -0
- data/lib/windows/sys/admin.rb +1018 -0
- data/spec/sys_admin_unix_spec.rb +262 -0
- data/spec/sys_admin_windows_spec.rb +350 -0
- data/sys-admin.gemspec +44 -0
- data.tar.gz.sig +0 -0
- metadata +162 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'sys/admin/custom'
|
2
|
+
require 'sys/admin/common'
|
3
|
+
|
4
|
+
# The Linux specific code.
|
5
|
+
|
6
|
+
module Sys
|
7
|
+
class Admin
|
8
|
+
# :no-doc:
|
9
|
+
BUF_MAX = 65536 # Absolute max buffer size for retry attempts.
|
10
|
+
private_constant :BUF_MAX
|
11
|
+
|
12
|
+
# I'm making some aliases here to prevent potential conflicts
|
13
|
+
attach_function :open_c, :open, [:string, :int], :int
|
14
|
+
attach_function :pread_c, :pread, [:int, :pointer, :size_t, :off_t], :size_t
|
15
|
+
attach_function :close_c, :close, [:int], :int
|
16
|
+
|
17
|
+
attach_function :getpwnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int
|
18
|
+
attach_function :getpwuid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int
|
19
|
+
attach_function :getpwent_r, [:pointer, :pointer, :size_t, :pointer], :int
|
20
|
+
attach_function :getgrent_r, [:pointer, :pointer, :size_t, :pointer], :int
|
21
|
+
attach_function :getgrnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int
|
22
|
+
attach_function :getgrgid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int
|
23
|
+
|
24
|
+
private_class_method :getgrent_r, :getgrnam_r, :getgrgid_r
|
25
|
+
private_class_method :open_c, :pread_c, :close_c
|
26
|
+
|
27
|
+
# struct passwd from /usr/include/pwd.h
|
28
|
+
class PasswdStruct < FFI::Struct
|
29
|
+
layout(
|
30
|
+
:pw_name, :string,
|
31
|
+
:pw_passwd, :string,
|
32
|
+
:pw_uid, :uint,
|
33
|
+
:pw_gid, :uint,
|
34
|
+
:pw_gecos, :string,
|
35
|
+
:pw_dir, :string,
|
36
|
+
:pw_shell, :string
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
private_constant :PasswdStruct
|
41
|
+
|
42
|
+
# struct group from /usr/include/grp.h
|
43
|
+
class GroupStruct < FFI::Struct
|
44
|
+
layout(
|
45
|
+
:gr_name, :string,
|
46
|
+
:gr_passwd, :string,
|
47
|
+
:gr_gid, :uint,
|
48
|
+
:gr_mem, :pointer
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
private_constant :GroupStruct
|
53
|
+
|
54
|
+
# I'm blending the timeval struct in directly here
|
55
|
+
class LastlogStruct < FFI::Struct
|
56
|
+
layout(
|
57
|
+
:ll_time, :uint,
|
58
|
+
:ll_line, [:char, 32],
|
59
|
+
:ll_host, [:char, 256]
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
private_constant :LastlogStruct
|
64
|
+
|
65
|
+
# Returns the login for the current process.
|
66
|
+
#
|
67
|
+
def self.get_login
|
68
|
+
get_user(geteuid()).name
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns a User object for the given name or uid. Raises an error
|
72
|
+
# if a user cannot be found.
|
73
|
+
#
|
74
|
+
# Examples:
|
75
|
+
#
|
76
|
+
# Sys::Admin.get_user('joe')
|
77
|
+
# Sys::Admin.get_user(501)
|
78
|
+
#
|
79
|
+
def self.get_user(uid)
|
80
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
81
|
+
pbuf = FFI::MemoryPointer.new(PasswdStruct)
|
82
|
+
temp = PasswdStruct.new
|
83
|
+
|
84
|
+
if uid.is_a?(String)
|
85
|
+
if getpwnam_r(uid, temp, buf, buf.size, pbuf) != 0
|
86
|
+
raise Error, "getpwnam_r function failed: " + strerror(FFI.errno)
|
87
|
+
end
|
88
|
+
else
|
89
|
+
if getpwuid_r(uid, temp, buf, buf.size, pbuf) != 0
|
90
|
+
raise Error, "getpwuid_r function failed: " + strerror(FFI.errno)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
ptr = pbuf.read_pointer
|
95
|
+
|
96
|
+
if ptr.null?
|
97
|
+
raise Error, "no user found for #{uid}"
|
98
|
+
end
|
99
|
+
|
100
|
+
pwd = PasswdStruct.new(ptr)
|
101
|
+
get_user_from_struct(pwd)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a Group object for the given name or uid. Raises an error
|
105
|
+
# if a group cannot be found.
|
106
|
+
#
|
107
|
+
# Examples:
|
108
|
+
#
|
109
|
+
# Sys::Admin.get_group('admin')
|
110
|
+
# Sys::Admin.get_group(101)
|
111
|
+
#--
|
112
|
+
# For groups with a large number of members we retry, allocating another
|
113
|
+
# 1k on each retry attempt, up to a maximum of 64k, which ought to be way
|
114
|
+
# more than you'll ever need.
|
115
|
+
#
|
116
|
+
def self.get_group(gid)
|
117
|
+
size = 1024
|
118
|
+
buf = FFI::MemoryPointer.new(:char, size)
|
119
|
+
pbuf = FFI::MemoryPointer.new(PasswdStruct)
|
120
|
+
temp = GroupStruct.new
|
121
|
+
|
122
|
+
begin
|
123
|
+
if gid.is_a?(String)
|
124
|
+
val = getgrnam_r(gid, temp, buf, buf.size, pbuf)
|
125
|
+
fun = 'getgrnam_r'
|
126
|
+
else
|
127
|
+
val = getgrgid_r(gid, temp, buf, buf.size, pbuf)
|
128
|
+
fun = 'getgrgid_r'
|
129
|
+
end
|
130
|
+
raise SystemCallError.new(fun, val) if val != 0
|
131
|
+
rescue Errno::ERANGE # Large groups
|
132
|
+
size += 1024
|
133
|
+
raise if size > BUF_MAX
|
134
|
+
buf = FFI::MemoryPointer.new(:char, size)
|
135
|
+
retry
|
136
|
+
end
|
137
|
+
|
138
|
+
ptr = pbuf.read_pointer
|
139
|
+
|
140
|
+
if ptr.null?
|
141
|
+
raise Error, "no group found for '#{gid}'"
|
142
|
+
end
|
143
|
+
|
144
|
+
grp = GroupStruct.new(ptr)
|
145
|
+
get_group_from_struct(grp)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns an array of User objects for each user on the system.
|
149
|
+
#
|
150
|
+
def self.users
|
151
|
+
users = []
|
152
|
+
|
153
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
154
|
+
pbuf = FFI::MemoryPointer.new(PasswdStruct)
|
155
|
+
temp = PasswdStruct.new
|
156
|
+
|
157
|
+
begin
|
158
|
+
setpwent()
|
159
|
+
|
160
|
+
while getpwent_r(temp, buf, buf.size, pbuf) == 0
|
161
|
+
ptr = pbuf.read_pointer
|
162
|
+
|
163
|
+
break if ptr.null?
|
164
|
+
|
165
|
+
pwd = PasswdStruct.new(ptr)
|
166
|
+
users << get_user_from_struct(pwd)
|
167
|
+
end
|
168
|
+
ensure
|
169
|
+
endpwent()
|
170
|
+
end
|
171
|
+
|
172
|
+
users
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns an array of Group objects for each user on the system.
|
176
|
+
#
|
177
|
+
def self.groups
|
178
|
+
groups = []
|
179
|
+
|
180
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
181
|
+
pbuf = FFI::MemoryPointer.new(GroupStruct)
|
182
|
+
temp = GroupStruct.new
|
183
|
+
|
184
|
+
begin
|
185
|
+
setgrent()
|
186
|
+
|
187
|
+
while getgrent_r(temp, buf, buf.size, pbuf) == 0
|
188
|
+
ptr = pbuf.read_pointer
|
189
|
+
|
190
|
+
break if ptr.null?
|
191
|
+
|
192
|
+
grp = GroupStruct.new(ptr)
|
193
|
+
groups << get_group_from_struct(grp)
|
194
|
+
end
|
195
|
+
ensure
|
196
|
+
endgrent()
|
197
|
+
end
|
198
|
+
|
199
|
+
groups
|
200
|
+
end
|
201
|
+
|
202
|
+
# Takes a GroupStruct and converts it to a Group object.
|
203
|
+
def self.get_group_from_struct(grp)
|
204
|
+
Group.new do |g|
|
205
|
+
g.name = grp[:gr_name]
|
206
|
+
g.passwd = grp[:gr_passwd]
|
207
|
+
g.gid = grp[:gr_gid]
|
208
|
+
g.members = grp[:gr_mem].read_array_of_string
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
private_class_method :get_group_from_struct
|
213
|
+
|
214
|
+
# Takes a UserStruct and converts it to a User object.
|
215
|
+
def self.get_user_from_struct(pwd)
|
216
|
+
user = User.new do |u|
|
217
|
+
u.name = pwd[:pw_name]
|
218
|
+
u.passwd = pwd[:pw_passwd]
|
219
|
+
u.uid = pwd[:pw_uid]
|
220
|
+
u.gid = pwd[:pw_gid]
|
221
|
+
u.gecos = pwd[:pw_gecos]
|
222
|
+
u.dir = pwd[:pw_dir]
|
223
|
+
u.shell = pwd[:pw_shell]
|
224
|
+
end
|
225
|
+
|
226
|
+
log = get_lastlog_info(user.uid)
|
227
|
+
|
228
|
+
if log
|
229
|
+
login_device = log[:ll_line].to_s
|
230
|
+
login_host = log[:ll_host].to_s
|
231
|
+
|
232
|
+
user.login_time = Time.at(log[:ll_time]) if log[:ll_time] > 0
|
233
|
+
user.login_device = login_device unless login_device.empty?
|
234
|
+
user.login_host = login_host unless login_host.empty?
|
235
|
+
end
|
236
|
+
|
237
|
+
user
|
238
|
+
end
|
239
|
+
|
240
|
+
private_class_method :get_user_from_struct
|
241
|
+
|
242
|
+
# Note: it seems that Linux, or at least Ubuntu, does not track logins
|
243
|
+
# via GDM (Gnome Display Manager) for some reason, so this may not return
|
244
|
+
# anything useful.
|
245
|
+
#
|
246
|
+
# The use of pread was necessary here because it's a sparse file.
|
247
|
+
#
|
248
|
+
def self.get_lastlog_info(uid)
|
249
|
+
logfile = '/var/log/lastlog'
|
250
|
+
lastlog = LastlogStruct.new
|
251
|
+
|
252
|
+
begin
|
253
|
+
fd = open_c(logfile, File::RDONLY)
|
254
|
+
|
255
|
+
if fd != -1
|
256
|
+
bytes = pread_c(fd, lastlog, lastlog.size, uid * lastlog.size)
|
257
|
+
if bytes < 0
|
258
|
+
raise Error, "pread function failed: " + strerror(FFI.errno)
|
259
|
+
end
|
260
|
+
else
|
261
|
+
nil # Ignore, improper permissions
|
262
|
+
end
|
263
|
+
ensure
|
264
|
+
close_c(fd) if fd && fd >= 0
|
265
|
+
end
|
266
|
+
|
267
|
+
lastlog
|
268
|
+
end
|
269
|
+
|
270
|
+
private_class_method :get_lastlog_info
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'sys/admin/custom'
|
2
|
+
require 'sys/admin/common'
|
3
|
+
|
4
|
+
# The Solaris specific code.
|
5
|
+
|
6
|
+
module Sys
|
7
|
+
class Admin
|
8
|
+
# :no-doc:
|
9
|
+
BUF_MAX = 65536 # Max buffer size for retry.
|
10
|
+
private_constant :BUF_MAX
|
11
|
+
|
12
|
+
# I'm making some aliases here to prevent potential conflicts
|
13
|
+
attach_function :open_c, :open, [:string, :int], :int
|
14
|
+
attach_function :pread_c, :pread, [:int, :pointer, :size_t, :off_t], :size_t
|
15
|
+
attach_function :close_c, :close, [:int], :int
|
16
|
+
|
17
|
+
attach_function :getlogin_r, [:pointer, :size_t], :pointer
|
18
|
+
attach_function :getpwnam_r, [:string, :pointer, :pointer, :size_t], :pointer
|
19
|
+
attach_function :getpwuid_r, [:long, :pointer, :pointer, :size_t], :pointer
|
20
|
+
attach_function :getpwent_r, [:pointer, :pointer, :int], :pointer
|
21
|
+
attach_function :getgrent_r, [:pointer, :pointer, :int], :pointer
|
22
|
+
attach_function :getgrnam_r, [:string, :pointer, :pointer, :int], :pointer
|
23
|
+
attach_function :getgrgid_r, [:long, :pointer, :pointer, :int], :pointer
|
24
|
+
|
25
|
+
private_class_method :getlogin_r, :getpwnam_r, :getpwuid_r, :getpwent_r
|
26
|
+
private_class_method :getgrent_r, :getgrnam_r, :getgrgid_r
|
27
|
+
private_class_method :open_c, :pread_c, :close_c
|
28
|
+
|
29
|
+
# struct passwd from /usr/include/pwd.h
|
30
|
+
class PasswdStruct < FFI::Struct
|
31
|
+
layout(
|
32
|
+
:pw_name, :string,
|
33
|
+
:pw_passwd, :string,
|
34
|
+
:pw_uid, :uint,
|
35
|
+
:pw_gid, :uint,
|
36
|
+
:pw_age, :string,
|
37
|
+
:pw_comment, :string,
|
38
|
+
:pw_gecos, :string,
|
39
|
+
:pw_dir, :string,
|
40
|
+
:pw_shell, :string
|
41
|
+
)
|
42
|
+
end
|
43
|
+
|
44
|
+
private_constant :PasswdStruct
|
45
|
+
|
46
|
+
# struct group from /usr/include/grp.h
|
47
|
+
class GroupStruct < FFI::Struct
|
48
|
+
layout(
|
49
|
+
:gr_name, :string,
|
50
|
+
:gr_passwd, :string,
|
51
|
+
:gr_gid, :uint,
|
52
|
+
:gr_mem, :pointer
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
private_constant :GroupStruct
|
57
|
+
|
58
|
+
# I'm blending the timeval struct in directly here
|
59
|
+
class LastlogStruct < FFI::Struct
|
60
|
+
layout(
|
61
|
+
:ll_time, :uint,
|
62
|
+
:ll_line, [:char, 32],
|
63
|
+
:ll_host, [:char, 256]
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
private_constant :LastlogStruct
|
68
|
+
|
69
|
+
# Returns the login for the current process.
|
70
|
+
#
|
71
|
+
def self.get_login
|
72
|
+
buf = FFI::MemoryPointer.new(:char, 256)
|
73
|
+
|
74
|
+
ptr = getlogin_r(buf, buf.size)
|
75
|
+
|
76
|
+
if ptr.null?
|
77
|
+
raise Error, "getlogin_r function failed: " + strerror(FFI.errno)
|
78
|
+
end
|
79
|
+
|
80
|
+
buf.read_string
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a User object for the given name or uid. Raises an error
|
84
|
+
# if a user cannot be found.
|
85
|
+
#
|
86
|
+
# Examples:
|
87
|
+
#
|
88
|
+
# Sys::Admin.get_user('joe')
|
89
|
+
# Sys::Admin.get_user(501)
|
90
|
+
#
|
91
|
+
def self.get_user(uid)
|
92
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
93
|
+
temp = PasswdStruct.new
|
94
|
+
|
95
|
+
if uid.is_a?(String)
|
96
|
+
ptr = getpwnam_r(uid, temp, buf, buf.size)
|
97
|
+
else
|
98
|
+
ptr = getpwuid_r(uid, temp, buf, buf.size)
|
99
|
+
end
|
100
|
+
|
101
|
+
if ptr.null?
|
102
|
+
raise Error, "getpwnam_r or getpwuid_r function failed: " + strerror(FFI.errno)
|
103
|
+
end
|
104
|
+
|
105
|
+
pwd = PasswdStruct.new(ptr)
|
106
|
+
get_user_from_struct(pwd)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns a Group object for the given name or uid. Raises an error
|
110
|
+
# if a group cannot be found.
|
111
|
+
#
|
112
|
+
# Examples:
|
113
|
+
#
|
114
|
+
# Sys::Admin.get_group('admin')
|
115
|
+
# Sys::Admin.get_group(101)
|
116
|
+
#
|
117
|
+
def self.get_group(gid)
|
118
|
+
size = 1024
|
119
|
+
buf = FFI::MemoryPointer.new(:char, size)
|
120
|
+
temp = GroupStruct.new
|
121
|
+
|
122
|
+
begin
|
123
|
+
if gid.is_a?(String)
|
124
|
+
ptr = getgrnam_r(gid, temp, buf, buf.size)
|
125
|
+
fun = 'getgrnam_r'
|
126
|
+
else
|
127
|
+
ptr = getgrgid_r(gid, temp, buf, buf.size)
|
128
|
+
fun = 'getgrgid_r'
|
129
|
+
end
|
130
|
+
|
131
|
+
# SunOS distinguishes between a failed function call and a
|
132
|
+
# group that isn't found.
|
133
|
+
|
134
|
+
if ptr.null?
|
135
|
+
if FFI.errno > 0
|
136
|
+
raise SystemCallError.new(fun, FFI.errno)
|
137
|
+
else
|
138
|
+
raise Error, "group '#{gid}' not found"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
rescue Errno::ERANGE
|
142
|
+
size += 1024
|
143
|
+
raise if size > BUF_MAX
|
144
|
+
buf = FFI::MemoryPointer.new(:char, size)
|
145
|
+
retry
|
146
|
+
end
|
147
|
+
|
148
|
+
grp = GroupStruct.new(ptr)
|
149
|
+
get_group_from_struct(grp)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns an array of User objects for each user on the system.
|
153
|
+
#
|
154
|
+
def self.users
|
155
|
+
users = []
|
156
|
+
|
157
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
158
|
+
temp = PasswdStruct.new
|
159
|
+
|
160
|
+
begin
|
161
|
+
setpwent()
|
162
|
+
|
163
|
+
while !(ptr = getpwent_r(temp, buf, buf.size)).null?
|
164
|
+
break if ptr.null?
|
165
|
+
|
166
|
+
pwd = PasswdStruct.new(ptr)
|
167
|
+
users << get_user_from_struct(pwd)
|
168
|
+
end
|
169
|
+
ensure
|
170
|
+
endpwent()
|
171
|
+
end
|
172
|
+
|
173
|
+
users
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns an array of Group objects for each user on the system.
|
177
|
+
#
|
178
|
+
def self.groups
|
179
|
+
groups = []
|
180
|
+
|
181
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
182
|
+
temp = GroupStruct.new
|
183
|
+
|
184
|
+
begin
|
185
|
+
setgrent()
|
186
|
+
|
187
|
+
while !(ptr = getgrent_r(temp, buf, buf.size)).null?
|
188
|
+
break if ptr.null?
|
189
|
+
|
190
|
+
grp = GroupStruct.new(ptr)
|
191
|
+
groups << get_group_from_struct(grp)
|
192
|
+
end
|
193
|
+
ensure
|
194
|
+
endgrent()
|
195
|
+
end
|
196
|
+
|
197
|
+
groups
|
198
|
+
end
|
199
|
+
|
200
|
+
# Takes a GroupStruct and converts it to a Group object.
|
201
|
+
def self.get_group_from_struct(grp)
|
202
|
+
Group.new do |g|
|
203
|
+
g.name = grp[:gr_name]
|
204
|
+
g.passwd = grp[:gr_passwd]
|
205
|
+
g.gid = grp[:gr_gid]
|
206
|
+
g.members = grp[:gr_mem].read_array_of_string
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
private_class_method :get_group_from_struct
|
211
|
+
|
212
|
+
# Takes a UserStruct and converts it to a User object.
|
213
|
+
def self.get_user_from_struct(pwd)
|
214
|
+
user = User.new do |u|
|
215
|
+
u.name = pwd[:pw_name]
|
216
|
+
u.passwd = pwd[:pw_passwd]
|
217
|
+
u.uid = pwd[:pw_uid]
|
218
|
+
u.gid = pwd[:pw_gid]
|
219
|
+
u.gecos = pwd[:pw_gecos]
|
220
|
+
u.dir = pwd[:pw_dir]
|
221
|
+
u.shell = pwd[:pw_shell]
|
222
|
+
end
|
223
|
+
|
224
|
+
log = get_lastlog_info(user.uid)
|
225
|
+
|
226
|
+
if log
|
227
|
+
login_device = log[:ll_line].to_s
|
228
|
+
login_host = log[:ll_host].to_s
|
229
|
+
|
230
|
+
user.login_time = Time.at(log[:ll_time]) if log[:ll_time] > 0
|
231
|
+
user.login_device = login_device unless login_device.empty?
|
232
|
+
user.login_host = login_host unless login_host.empty?
|
233
|
+
end
|
234
|
+
|
235
|
+
user
|
236
|
+
end
|
237
|
+
|
238
|
+
private_class_method :get_use_from_struct
|
239
|
+
|
240
|
+
# The use of pread was necessary here because it's a sparse file. Note
|
241
|
+
# also that while Solaris supports the getuserattr function, it doesn't
|
242
|
+
# appear to store anything regarding login information.
|
243
|
+
#
|
244
|
+
def self.get_lastlog_info(uid)
|
245
|
+
logfile = '/var/adm/lastlog'
|
246
|
+
lastlog = LastlogStruct.new
|
247
|
+
|
248
|
+
begin
|
249
|
+
fd = open_c(logfile, File::RDONLY)
|
250
|
+
|
251
|
+
if fd != -1
|
252
|
+
bytes = pread_c(fd, lastlog, lastlog.size, uid * lastlog.size)
|
253
|
+
if bytes < 0
|
254
|
+
raise Error, "pread function failed: " + strerror(FFI.errno)
|
255
|
+
end
|
256
|
+
else
|
257
|
+
nil # Ignore, improper permissions
|
258
|
+
end
|
259
|
+
ensure
|
260
|
+
close_c(fd) if fd && fd >= 0
|
261
|
+
end
|
262
|
+
|
263
|
+
lastlog
|
264
|
+
end
|
265
|
+
|
266
|
+
private_class_method :get_lastlog_info
|
267
|
+
end
|
268
|
+
end
|