sys-admin 1.5.6 → 1.6.0

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