sys-admin 1.5.6 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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
- Admin.groups(options = {}){ |group| ... }
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
- Admin.users(options = {}){ |user| ... }
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-2010, Daniel J. Berger
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
- WINDOWS = CONFIG['host_os'] =~ /msdos|mswin|win32|mingw|cygwin|windows/i
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 a sys-admin gem file."
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-admin gem."
51
- task :install => [:create] do
52
- gem = Dir['*.gem'].first
53
- sh "gem install #{gem}"
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
- if WINDOWS
60
- t.libs << 'lib'
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
- task :test => :build
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