sys-admin 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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