sys-admin 1.7.0 → 1.7.1

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