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.
- data/CHANGES +8 -0
- data/MANIFEST +6 -3
- data/README +3 -17
- data/Rakefile +22 -51
- data/examples/example_groups.rb +27 -0
- data/examples/example_users.rb +41 -0
- data/lib/bsd/sys/admin.rb +239 -0
- data/lib/darwin/sys/admin.rb +227 -0
- data/lib/linux/sys/admin.rb +259 -0
- data/lib/sunos/sys/admin.rb +241 -0
- data/lib/sys/admin.rb +17 -0
- data/lib/sys/admin/common.rb +140 -0
- data/lib/sys/admin/custom.rb +16 -0
- data/lib/unix/sys/admin.rb +164 -0
- data/lib/windows/sys/admin.rb +971 -0
- data/sys-admin.gemspec +3 -2
- data/test/test_sys_admin.rb +3 -5
- data/test/test_sys_admin_unix.rb +96 -81
- data/test/test_sys_admin_windows.rb +16 -38
- metadata +67 -59
- data/examples/groups.rb +0 -39
- data/examples/users.rb +0 -53
- data/ext/extconf.rb +0 -72
- data/ext/sys/admin.c +0 -419
- data/ext/sys/admin.h +0 -515
data/CHANGES
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 1.6.0 - 5-Jan-2013
|
2
|
+
* Converted code to use FFI. This mostly only affects the unix flavors.
|
3
|
+
* The Admin.users and Admin.groups methods no longer accept a block.
|
4
|
+
* Some test suite updates.
|
5
|
+
* Because all code is now pure Ruby, there is longer any need for two
|
6
|
+
separate gems. There is now a single, unified gem that works on all
|
7
|
+
supported platforms.
|
8
|
+
|
1
9
|
== 1.5.6 - 30-Jul-2011
|
2
10
|
* Fixed issue for non-gnu platforms where it would use the wrong function
|
3
11
|
prototype because the Ruby core team took it upon themselves to explicitly
|
data/MANIFEST
CHANGED
@@ -5,10 +5,13 @@
|
|
5
5
|
* README
|
6
6
|
* examples/groups.rb
|
7
7
|
* examples/users.rb
|
8
|
-
* ext/admin.c
|
9
|
-
* ext/admin.h
|
10
|
-
* ext/extconf.rb
|
11
8
|
* lib/sys/admin.rb
|
9
|
+
* lib/darwin/sys/admin.rb
|
10
|
+
* lib/linux/sys/admin.rb
|
11
|
+
* lib/bsd/sys/admin.rb
|
12
|
+
* lib/sunos/sys/admin.rb
|
13
|
+
* lib/sys/admin/common.rb
|
14
|
+
* lib/sys/admin/custom.rb
|
12
15
|
* test/test_sys_admin.rb
|
13
16
|
* test/test_sys_admin_unix.rb
|
14
17
|
* test/test_sys_admin_windows.rb
|
data/README
CHANGED
@@ -8,19 +8,9 @@
|
|
8
8
|
require 'sys/admin'
|
9
9
|
include Sys
|
10
10
|
|
11
|
-
# Yields a User object for each user
|
12
|
-
Admin.users{ |user|
|
13
|
-
p user
|
14
|
-
}
|
15
|
-
|
16
11
|
# Returns an Array of User objects
|
17
12
|
a = Admin.users
|
18
13
|
|
19
|
-
# Yields a Group object for each group
|
20
|
-
Admin.groups{ |group|
|
21
|
-
p group
|
22
|
-
}
|
23
|
-
|
24
14
|
# Returns an Array of Group objects
|
25
15
|
g = Admin.groups
|
26
16
|
|
@@ -47,17 +37,13 @@ Admin.get_group(gid, options = {})
|
|
47
37
|
options you provide, e.g. 'domain' or 'localaccount'.
|
48
38
|
|
49
39
|
Admin.groups(options = {})
|
50
|
-
|
51
|
-
In block form, yields a Group object for each user on the system. In
|
52
|
-
non-block form, returns an Array of Group objects.
|
40
|
+
Returns an Array of Group objects.
|
53
41
|
|
54
42
|
The +options+ hash is for MS Windows only, and allows you to restrict the
|
55
43
|
search based on the options you provide, e.g. 'domain' or 'localaccount'.
|
56
44
|
|
57
45
|
Admin.users(options = {})
|
58
|
-
|
59
|
-
In block form, yields a User object for each user on the system. In
|
60
|
-
non-block form, returns an Array of User objects.
|
46
|
+
Returns an Array of User objects.
|
61
47
|
|
62
48
|
The +options+ hash is for MS Windows only, and allows you to restrict the
|
63
49
|
search based on the options you provide, e.g. 'domain' or 'localaccount'.
|
@@ -149,7 +135,7 @@ Admin::Error < StandardError
|
|
149
135
|
Artistic 2.0
|
150
136
|
|
151
137
|
== Copyright
|
152
|
-
(C) 2005-
|
138
|
+
(C) 2005-2013, Daniel J. Berger
|
153
139
|
All Rights Reserved
|
154
140
|
|
155
141
|
== Author
|
data/Rakefile
CHANGED
@@ -2,73 +2,44 @@ require 'rake'
|
|
2
2
|
require 'rake/clean'
|
3
3
|
require 'rake/testtask'
|
4
4
|
require 'rbconfig'
|
5
|
-
include Config
|
6
5
|
|
7
|
-
|
8
|
-
|
9
|
-
CLEAN.include(
|
10
|
-
'**/*.gem', # Gem files
|
11
|
-
'**/*.rbc', # Rubinius
|
12
|
-
'**/*.o', # C object file
|
13
|
-
'**/*.log', # Ruby extension build log
|
14
|
-
'**/Makefile', # C Makefile
|
15
|
-
'**/conftest.dSYM', # OS X build directory
|
16
|
-
"**/*.#{CONFIG['DLEXT']}" # C shared object
|
17
|
-
)
|
18
|
-
|
19
|
-
desc "Build the sys-admin library on UNIX systems (but don't install it)"
|
20
|
-
task :build => [:clean] do
|
21
|
-
unless WINDOWS
|
22
|
-
Dir.chdir('ext') do
|
23
|
-
ruby 'extconf.rb'
|
24
|
-
sh 'make'
|
25
|
-
build_file = 'admin.' + Config::CONFIG['DLEXT']
|
26
|
-
FileUtils.cp(build_file, 'sys')
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
6
|
+
CLEAN.include("**/*.gem", "**/*.rbx", "**/*.rbc")
|
30
7
|
|
31
8
|
namespace :gem do
|
32
|
-
desc "Create
|
9
|
+
desc "Create the sys-uname gem"
|
33
10
|
task :create => [:clean] do
|
34
11
|
spec = eval(IO.read('sys-admin.gemspec'))
|
35
|
-
|
36
|
-
if WINDOWS
|
37
|
-
spec.platform = Gem::Platform::CURRENT
|
38
|
-
spec.platform.cpu = 'universal'
|
39
|
-
spec.files = spec.files.reject{ |f| f.include?('ext') }
|
40
|
-
spec.add_dependency('win32-security', '>= 0.1.2')
|
41
|
-
else
|
42
|
-
spec.files = spec.files.reject{ |f| f.include?('lib') }
|
43
|
-
spec.extensions = ['ext/extconf.rb']
|
44
|
-
spec.extra_rdoc_files << 'ext/sys/admin.c'
|
45
|
-
end
|
46
|
-
|
47
12
|
Gem::Builder.new(spec).build
|
48
13
|
end
|
49
14
|
|
50
|
-
desc "Install the sys-
|
51
|
-
task :install => [:
|
52
|
-
|
53
|
-
sh "gem install #{
|
15
|
+
desc "Install the sys-uname gem"
|
16
|
+
task :install => [:build] do
|
17
|
+
file = Dir["*.gem"].first
|
18
|
+
sh "gem install #{file}"
|
54
19
|
end
|
55
20
|
end
|
56
21
|
|
57
|
-
desc "Run the test suite"
|
58
22
|
Rake::TestTask.new('test') do |t|
|
59
|
-
|
60
|
-
|
23
|
+
case RbConfig::CONFIG['host_os']
|
24
|
+
when /darwin|osx/i
|
25
|
+
t.libs << 'lib/darwin'
|
26
|
+
when /linux/i
|
27
|
+
t.libs << 'lib/linux'
|
28
|
+
when /sunos|solaris/i
|
29
|
+
t.libs << 'lib/sunos'
|
30
|
+
when /bsd/i
|
31
|
+
t.libs << 'lib/bsd'
|
32
|
+
when /windows|win32|mingw|cygwin|dos/i
|
33
|
+
t.libs << 'lib/windows'
|
61
34
|
else
|
62
|
-
|
63
|
-
t.libs << 'ext'
|
64
|
-
t.libs.delete('lib')
|
35
|
+
t.libs << 'lib/unix'
|
65
36
|
end
|
37
|
+
|
38
|
+
t.warning = true
|
39
|
+
t.verbose = true
|
40
|
+
|
66
41
|
t.libs << 'test'
|
67
42
|
t.test_files = FileList['test/test_sys_admin.rb']
|
68
43
|
end
|
69
44
|
|
70
|
-
task :test do
|
71
|
-
Rake.application[:clean].execute
|
72
|
-
end
|
73
|
-
|
74
45
|
task :default => :test
|
@@ -0,0 +1,27 @@
|
|
1
|
+
###########################################################################
|
2
|
+
# groups.rb
|
3
|
+
#
|
4
|
+
# Sample script to demonstrate some of the various group methods. Alter
|
5
|
+
# as you see fit.
|
6
|
+
###########################################################################
|
7
|
+
require "pp"
|
8
|
+
require "sys/admin"
|
9
|
+
include Sys
|
10
|
+
|
11
|
+
if File::ALT_SEPARATOR
|
12
|
+
pp Admin.get_group("guests")
|
13
|
+
pp Admin.get_group(513)
|
14
|
+
else
|
15
|
+
pp Admin.get_group("admin")
|
16
|
+
pp Admin.get_group(7)
|
17
|
+
end
|
18
|
+
|
19
|
+
sleep 3
|
20
|
+
|
21
|
+
Admin.groups.each{ |g|
|
22
|
+
pp g
|
23
|
+
puts
|
24
|
+
}
|
25
|
+
|
26
|
+
# This should raise an error
|
27
|
+
Admin.get_group("fofofofof")
|
@@ -0,0 +1,41 @@
|
|
1
|
+
###########################################################################
|
2
|
+
# users.rb
|
3
|
+
#
|
4
|
+
# Sample script to demonstrate some of the various user methods. Alter
|
5
|
+
# as you see fit.
|
6
|
+
###########################################################################
|
7
|
+
require "pp"
|
8
|
+
require "sys/admin"
|
9
|
+
include Sys
|
10
|
+
|
11
|
+
=begin
|
12
|
+
user = User.new do |u|
|
13
|
+
u.name = "Foo"
|
14
|
+
u.description = "Test account"
|
15
|
+
u.password = "changeme"
|
16
|
+
#u.lockout = false
|
17
|
+
u.disabled = true
|
18
|
+
#u.password_required = true
|
19
|
+
end
|
20
|
+
|
21
|
+
Admin.delete_user(u.name) rescue nil
|
22
|
+
Admin.add_user(user)
|
23
|
+
|
24
|
+
pp Admin.get_user("Foo")
|
25
|
+
|
26
|
+
Admin.delete_user("Foo")
|
27
|
+
=end
|
28
|
+
|
29
|
+
user = Admin.get_login
|
30
|
+
|
31
|
+
puts "User: #{user}"
|
32
|
+
|
33
|
+
sleep 3
|
34
|
+
|
35
|
+
Admin.users.each{ |u|
|
36
|
+
pp u
|
37
|
+
puts
|
38
|
+
}
|
39
|
+
|
40
|
+
pp Admin.get_user(user)
|
41
|
+
pp Admin.get_user(501)
|
@@ -0,0 +1,239 @@
|
|
1
|
+
require 'sys/admin/custom'
|
2
|
+
require 'sys/admin/common'
|
3
|
+
|
4
|
+
# The BSD 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, :int], :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 :getgrnam_r, [:string, :pointer, :pointer, :size_t, :pointer], :int
|
19
|
+
attach_function :getgrgid_r, [:long, :pointer, :pointer, :size_t, :pointer], :int
|
20
|
+
|
21
|
+
private_class_method :getlogin_r, :getpwnam_r, :getpwuid_r, :getgrnam_r
|
22
|
+
private_class_method :getgrgid_r
|
23
|
+
private_class_method :open_c, :pread_c, :close_c
|
24
|
+
|
25
|
+
# struct passwd from /usr/include/pwd.h
|
26
|
+
class PasswdStruct < FFI::Struct
|
27
|
+
layout(
|
28
|
+
:pw_name, :string,
|
29
|
+
:pw_passwd, :string,
|
30
|
+
:pw_uid, :uint,
|
31
|
+
:pw_gid, :uint,
|
32
|
+
:pw_change, :ulong,
|
33
|
+
:pw_class, :string,
|
34
|
+
:pw_gecos, :string,
|
35
|
+
:pw_dir, :string,
|
36
|
+
:pw_shell, :string,
|
37
|
+
:pw_expire, :ulong
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
# struct group from /usr/include/grp.h
|
42
|
+
class GroupStruct < FFI::Struct
|
43
|
+
layout(
|
44
|
+
:gr_name, :string,
|
45
|
+
:gr_passwd, :string,
|
46
|
+
:gr_gid, :uint,
|
47
|
+
:gr_mem, :pointer
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
51
|
+
# I'm blending the timeval struct in directly here
|
52
|
+
class LastlogStruct < FFI::Struct
|
53
|
+
layout(
|
54
|
+
:ll_time, :int32,
|
55
|
+
:ll_line, [:char, 32],
|
56
|
+
:ll_host, [:char, 256]
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
public
|
61
|
+
|
62
|
+
# Returns the login for the current process.
|
63
|
+
#
|
64
|
+
def self.get_login
|
65
|
+
buf = FFI::MemoryPointer.new(:char, 256)
|
66
|
+
|
67
|
+
if getlogin_r(buf, buf.size) != 0
|
68
|
+
raise Error, "getlogin_r function failed: " + strerror(FFI.errno)
|
69
|
+
end
|
70
|
+
|
71
|
+
buf.read_string
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns a User object for the given name or uid. Raises an error
|
75
|
+
# if a user cannot be found.
|
76
|
+
#
|
77
|
+
# Examples:
|
78
|
+
#
|
79
|
+
# Sys::Admin.get_user('joe')
|
80
|
+
# Sys::Admin.get_user(501)
|
81
|
+
#
|
82
|
+
def self.get_user(uid)
|
83
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
84
|
+
pbuf = FFI::MemoryPointer.new(PasswdStruct)
|
85
|
+
temp = PasswdStruct.new
|
86
|
+
|
87
|
+
if uid.is_a?(String)
|
88
|
+
if getpwnam_r(uid, temp, buf, buf.size, pbuf) != 0
|
89
|
+
raise Error, "getpwnam_r function failed: " + strerror(FFI.errno)
|
90
|
+
end
|
91
|
+
else
|
92
|
+
if getpwuid_r(uid, temp, buf, buf.size, pbuf) != 0
|
93
|
+
raise Error, "getpwuid_r function failed: " + strerror(FFI.errno)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
ptr = pbuf.read_pointer
|
98
|
+
|
99
|
+
if ptr.null?
|
100
|
+
raise Error, "no user found for #{uid}"
|
101
|
+
end
|
102
|
+
|
103
|
+
pwd = PasswdStruct.new(ptr)
|
104
|
+
get_user_from_struct(pwd)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns a Group object for the given name or uid. Raises an error
|
108
|
+
# if a group cannot be found.
|
109
|
+
#
|
110
|
+
# Examples:
|
111
|
+
#
|
112
|
+
# Sys::Admin.get_group('admin')
|
113
|
+
# Sys::Admin.get_group(101)
|
114
|
+
#
|
115
|
+
def self.get_group(gid)
|
116
|
+
buf = FFI::MemoryPointer.new(:char, 1024)
|
117
|
+
pbuf = FFI::MemoryPointer.new(PasswdStruct)
|
118
|
+
temp = GroupStruct.new
|
119
|
+
|
120
|
+
if gid.is_a?(String)
|
121
|
+
if getgrnam_r(gid, temp, buf, buf.size, pbuf) != 0
|
122
|
+
raise Error, "getgrnam_r function failed: " + strerror(FFI.errno)
|
123
|
+
end
|
124
|
+
else
|
125
|
+
if getgrgid_r(gid, temp, buf, buf.size, pbuf) != 0
|
126
|
+
raise Error, "getgrgid_r function failed: " + strerror(FFI.errno)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
ptr = pbuf.read_pointer
|
131
|
+
|
132
|
+
if ptr.null?
|
133
|
+
raise Error, "no group found for #{gid}"
|
134
|
+
end
|
135
|
+
|
136
|
+
grp = GroupStruct.new(ptr)
|
137
|
+
get_group_from_struct(grp)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns an array of User objects for each user on the system.
|
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[:ll_time])
|
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
|
+
# Get lastlog information for the given user.
|
217
|
+
def self.get_lastlog_info(uid)
|
218
|
+
logfile = '/var/log/lastlog'
|
219
|
+
lastlog = LastlogStruct.new
|
220
|
+
|
221
|
+
begin
|
222
|
+
fd = open_c(logfile, File::RDONLY)
|
223
|
+
|
224
|
+
if fd != -1
|
225
|
+
bytes = pread_c(fd, lastlog, lastlog.size, uid * lastlog.size)
|
226
|
+
if bytes < 0
|
227
|
+
raise Error, "pread function failed: " + strerror(FFI.errno)
|
228
|
+
end
|
229
|
+
else
|
230
|
+
nil # Ignore, improper permissions
|
231
|
+
end
|
232
|
+
ensure
|
233
|
+
close_c(fd) if fd && fd >= 0
|
234
|
+
end
|
235
|
+
|
236
|
+
lastlog
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|