sys-proctable 1.0.0-universal-darwin → 1.1.0-universal-darwin

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d71681f166b70bdec46497d235b4d47c9061d895
4
- data.tar.gz: 8b36e0e52bd558ce1f8c610cee8c2952a55136e1
3
+ metadata.gz: 138368be13088153dc725009fa1921edf64a7a63
4
+ data.tar.gz: a046d5a49a5071add7cb27ca153692dacf5234ff
5
5
  SHA512:
6
- metadata.gz: 056009b1616215f1f795596cb11ce97a16da140549f7ce4494fbd150be9c46fb3e0b13eaf69fbf84ab3516d0a00f725713e1ef51a131d9d8bd8c8f4cf1b9529c
7
- data.tar.gz: 1ccc358831b19224de154589c00e99e8a87a335fec8b0a66cb57052fc064bd2fc2dd57b282d4ebbefddb44fb5c89418f94278df0dacb8d5e1e756818df61bd3b
6
+ metadata.gz: 0a75f3fc8d2190f04bf05e9ee0859a9c1e91321eb08980af50a5f990c93790f3482edb1c3218f6557e9667d565268d0a69352916898276738fbc2d45e8afaa1f
7
+ data.tar.gz: c758a8a578c3ec13230b88f2d5801516ce57fee59d6352df1f7fcd2b6419046ec946d8e9eeaf0d9e281c588a1927bf54d3d74929add706fa0450f67d116efc71
Binary file
data.tar.gz.sig CHANGED
Binary file
data/CHANGES CHANGED
@@ -1,6 +1,16 @@
1
+ == 1.1.0 - 27-Jun-2016
2
+ * License was changed to Apache 2.0.
3
+ * The OS X version now requires OS X 10.7 or later.
4
+ * Scrapped the C implementation for OS X, and replaced it with a libproc wrapper
5
+ using FFI, so it is now pure Ruby. Note that some new struct members have been
6
+ added, while others have been dropped. This includes pctcpu, so Sys::Top does
7
+ not work at the moment.
8
+ * Rakefile updates to accomodate the changes for OS X.
9
+ * Some test suite updates and refactoring.
10
+
1
11
  == 1.0.0 - 11-Jan-2016
2
12
  * Added smaps information for Linux. Thanks go to Joe Rafaniello for the patch.
3
- * This is not really a major release. I just ran out of version numbers.
13
+ * This really should not have been a major release, sorry.
4
14
 
5
15
  == 0.9.9 - 8-Nov-2015
6
16
  * Added support for cgroups on Linux. Thanks go to Dennis Günnewig for the patch.
data/MANIFEST CHANGED
@@ -11,14 +11,13 @@
11
11
  * doc/top.txt
12
12
  * doc/windows.txt
13
13
  * example/example_ps.rb
14
- * ext/darwin/extconf.rb
15
- * ext/darwin/sys/proctable.c
16
14
  * ext/hpux/extconf.rb
17
15
  * ext/hpux/sys/proctable.c
18
16
  * lib/sys-proctable.rb
19
17
  * lib/sys/top.rb
20
18
  * lib/sys/proctable/version.rb
21
19
  * lib/aix/sys/proctable.rb
20
+ * lib/darwin/sys/proctable.rb
22
21
  * lib/freebsd/sys/proctable.rb
23
22
  * lib/linux/sys/proctable.rb
24
23
  * lib/sunos/sys/proctable.rb
data/README CHANGED
@@ -10,7 +10,7 @@
10
10
  * FreeBSD
11
11
  * Solaris 8+
12
12
  * HP-UX 10+
13
- * OS X 10.4+
13
+ * OS X 10.7+
14
14
  * AIX 5.3+
15
15
 
16
16
  == Installation
@@ -22,6 +22,7 @@
22
22
  gem install sys-proctable --platform sunos # Solaris
23
23
  gem install sys-proctable --platform linux # Linux
24
24
  gem install sys-proctable --platform freebsd # FreeBSD
25
+ gem install sys-proctable --platform darwin # OS X
25
26
 
26
27
  == Synopsis
27
28
  require 'sys/proctable'
@@ -60,16 +61,13 @@
60
61
  The cmdline member on Solaris is limited to 80 characters unless you (or
61
62
  your program) own the process. This is a Solaris design flaw/feature.
62
63
 
63
- === Thread Safety
64
- I am not currently using a thread-safe version of readdir() for versions
65
- of this library that use C. I am not especially concerned about it either.
66
- If you are trying to read information out of /proc from different threads
67
- at the same time there is something seriously wrong with your code logic.
68
- Using readdir_r() still won't solve all potential thread safety issues anyway.
64
+ === OS X
65
+ The libproc interface is used. That means you will only get list of
66
+ processes that you have access to. To get a full listing, run as root.
69
67
 
70
68
  == Future Plans
71
69
  Add support for NetBSD and OpenBSD.
72
- Convert existing C code to FFI.
70
+ Convert remaining C code (just HP-UX at this point) to FFI.
73
71
 
74
72
  == Acknowledgements
75
73
  This library was originally based on the Perl module Proc::ProcessTable
@@ -87,7 +85,7 @@
87
85
  debugging help.
88
86
 
89
87
  Thanks go to David Felstead for the original OS X code. Thanks also go
90
- to Matthias Zirnstein for adding cmdline support for OS X.
88
+ to Matthias Zirnstein for adding the original cmdline support for OS X.
91
89
 
92
90
  Finally I'd like to thank all the folks who have submitted bug reports
93
91
  and/or patches.
@@ -102,17 +100,11 @@
102
100
  including platform specific notes and issues.
103
101
 
104
102
  == License
105
- Artistic 2.0
103
+ Apache 2.0
106
104
 
107
105
  == Copyright
108
106
  (C) 2003-2016 Daniel J. Berger
109
107
  All Rights Reserved.
110
108
 
111
- == Contributions
112
- Although this library is free, please consider having your company
113
- setup a gittip if used by your company professionally.
114
-
115
- http://www.gittip.com/djberg96/
116
-
117
109
  == Author
118
110
  Daniel J. Berger
data/Rakefile CHANGED
@@ -27,15 +27,12 @@ task :build => [:clean] do
27
27
  end
28
28
 
29
29
  case CONFIG['host_os']
30
- when /darwin/i
31
- dir = 'ext/darwin'
32
- ext = '.bundle'
33
30
  when /hpux/i
34
31
  dir = 'ext/hpux'
35
32
  ext = '.sl'
36
33
  end
37
34
 
38
- if CONFIG['host_os'] =~ /darwin|hpux/i
35
+ if CONFIG['host_os'] =~ /hpux/i
39
36
  Dir.chdir(dir) do
40
37
  ruby 'extconf.rb'
41
38
  sh 'make'
@@ -63,7 +60,7 @@ task :install => [:build] do
63
60
  when /freebsd/i
64
61
  file = 'lib/freebsd/sys/proctable.rb'
65
62
  when /darwin/i
66
- Dir.chdir('ext/darwin'){ sh 'make install' }
63
+ file = 'lib/darwin/sys/proctable.rb'
67
64
  when /hpux/i
68
65
  Dir.chdir('ext/hpux'){ sh 'make install' }
69
66
  end
@@ -74,7 +71,7 @@ end
74
71
  desc 'Uninstall the sys-proctable library'
75
72
  task :uninstall do
76
73
  case CONFIG['host_os']
77
- when /darwin|hpux/i
74
+ when /hpux/i
78
75
  dir = File.join(CONFIG['sitearchdir'], 'sys')
79
76
  file = File.join(dir, 'proctable.' + CONFIG['DLEXT'])
80
77
  else
@@ -117,7 +114,7 @@ Rake::TestTask.new do |t|
117
114
  t.test_files = FileList['test/test_sys_proctable_freebsd.rb']
118
115
  t.libs << 'lib/freebsd'
119
116
  when /darwin/i
120
- t.libs << 'ext/darwin'
117
+ t.libs << 'lib/darwin'
121
118
  t.test_files = FileList['test/test_sys_proctable_darwin.rb']
122
119
  when /hpux/i
123
120
  t.libs << 'ext/hpux'
@@ -153,10 +150,10 @@ namespace :gem do
153
150
  spec.add_dependency('ffi')
154
151
  when /darwin/i
155
152
  spec.platform = Gem::Platform.new(['universal', 'darwin'])
156
- spec.files << 'ext/darwin/sys/proctable.c'
157
- spec.extra_rdoc_files << 'ext/darwin/sys/proctable.c'
153
+ spec.require_paths = ['lib', 'lib/darwin']
154
+ spec.files += ['lib/darwin/sys/proctable.rb']
158
155
  spec.test_files << 'test/test_sys_proctable_darwin.rb'
159
- spec.extensions = ['ext/darwin/extconf.rb']
156
+ spec.add_dependency('ffi')
160
157
  when /hpux/i
161
158
  spec.platform = Gem::Platform.new(['universal', 'hpux'])
162
159
  spec.files << 'ext/hpux/sys/proctable.c'
@@ -0,0 +1,241 @@
1
+ require 'sys/proctable/version'
2
+ require 'ffi'
3
+
4
+ module Sys
5
+ class ProcTable
6
+ extend FFI::Library
7
+
8
+ # Error typically raised if the ProcTable.ps method fails.
9
+ class Error < StandardError; end
10
+
11
+ # There is no constructor
12
+ private_class_method :new
13
+
14
+ private
15
+
16
+ PROC_ALL_PIDS = 1
17
+ PROC_PIDTASKALLINFO = 2
18
+ PROC_PIDTASKINFO = 4
19
+
20
+ CTL_KERN = 1
21
+ KERN_PROCARGS = 38
22
+ KERN_PROCARGS2 = 49
23
+ MAXCOMLEN = 16
24
+ MAXPATHLEN = 256
25
+
26
+ PROC_PIDPATHINFO_MAXSIZE = MAXPATHLEN * 4
27
+
28
+ class ProcBsdInfo < FFI::Struct
29
+ layout(
30
+ :pbi_flags, :uint32_t,
31
+ :pbi_status, :uint32_t,
32
+ :pbi_xstatus, :uint32_t,
33
+ :pbi_pid, :uint32_t,
34
+ :pbi_ppid, :uint32_t,
35
+ :pbi_uid, :uid_t,
36
+ :pbi_gid, :uid_t,
37
+ :pbi_ruid, :uid_t,
38
+ :pbi_rgid, :gid_t,
39
+ :pbi_svuid, :uid_t,
40
+ :pbi_svgid, :gid_t,
41
+ :rfu1, :uint32_t,
42
+ :pbi_comm, [:char, MAXCOMLEN],
43
+ :pbi_name, [:char, MAXCOMLEN * 2],
44
+ :pbi_nfiles, :uint32_t,
45
+ :pbi_pgid, :uint32_t,
46
+ :pbi_pjobc, :uint32_t,
47
+ :e_tdev, :uint32_t,
48
+ :e_tpgid, :uint32_t,
49
+ :pbi_nice, :int32_t,
50
+ :pbi_start_tvsec, :uint64_t,
51
+ :pbi_start_tvusec, :uint64_t
52
+ )
53
+ end
54
+
55
+ class ProcTaskInfo < FFI::Struct
56
+ layout(
57
+ :pti_virtual_size, :uint64_t,
58
+ :pti_resident_size, :uint64_t,
59
+ :pti_total_user, :uint64_t,
60
+ :pti_total_system, :uint64_t,
61
+ :pti_threads_user, :uint64_t,
62
+ :pti_threads_system, :uint64_t,
63
+ :pti_policy, :int32_t,
64
+ :pti_faults, :int32_t,
65
+ :pti_pageins, :int32_t,
66
+ :pti_cow_faults, :int32_t,
67
+ :pti_messages_sent, :int32_t,
68
+ :pti_messages_received, :int32_t,
69
+ :pti_syscalls_mach, :int32_t,
70
+ :pti_syscalls_unix, :int32_t,
71
+ :pti_csw, :int32_t,
72
+ :pti_threadnum, :int32_t,
73
+ :pti_numrunning, :int32_t,
74
+ :pti_priority, :int32_t
75
+ )
76
+ end
77
+
78
+ class ProcTaskAllInfo < FFI::Struct
79
+ layout(:pbsd, ProcBsdInfo, :ptinfo, ProcTaskInfo)
80
+ end
81
+
82
+ ffi_lib 'proc'
83
+
84
+ attach_function :proc_listallpids, [:pointer, :int], :int
85
+ attach_function :proc_pidinfo, [:int, :int, :uint64_t, :pointer, :int], :int
86
+
87
+ ffi_lib FFI::Library::LIBC
88
+
89
+ attach_function :sysctl, [:pointer, :uint, :pointer, :pointer, :pointer, :size_t], :int
90
+
91
+ # These mostly mimic the struct members, but we've added a few custom ones as well.
92
+ @fields = %w[
93
+ flags status xstatus pid ppid uid gid ruid rgid svuid svgid rfu1 comm
94
+ name nfiles pgid pjobc tdev tpgid nice start_tvsec start_tvusec
95
+ virtual_size resident_size total_user total_system threads_user
96
+ threads_system policy faults pageins cow_faults messages_sent
97
+ messages_received syscalls_mach syscalls_unix csw threadnum numrunning
98
+ priority cmdline exe environ
99
+ ]
100
+
101
+ # Add a couple aliases to make it similar to Linux
102
+ ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
103
+ alias vsize virtual_size
104
+ alias rss resident_size
105
+ end
106
+
107
+ public
108
+
109
+ # Returns an array of fields that each ProcTableStruct will contain. This
110
+ # may be useful if you want to know in advance what fields are available
111
+ # without having to perform at least one read of the process table.
112
+ #
113
+ # Example:
114
+ #
115
+ # Sys::ProcTable.fields.each{ |field|
116
+ # puts "Field: #{field}"
117
+ # }
118
+ #
119
+ def self.fields
120
+ @fields
121
+ end
122
+
123
+ # In block form, yields a ProcTableStruct for each process entry that you
124
+ # have rights to. This method returns an array of ProcTableStruct's in
125
+ # non-block form.
126
+ #
127
+ # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
128
+ # returned, or nil if no process information is found for that +pid+.
129
+ #
130
+ # Example:
131
+ #
132
+ # # Iterate over all processes
133
+ # ProcTable.ps do |proc_info|
134
+ # p proc_info
135
+ # end
136
+ #
137
+ # # Print process table information for only pid 1001
138
+ # p ProcTable.ps(1001)
139
+ #
140
+ def self.ps(pid = nil)
141
+ raise TypeError unless pid.is_a?(Fixnum) if pid
142
+
143
+ num = proc_listallpids(nil, 0)
144
+ ptr = FFI::MemoryPointer.new(:pid_t, num)
145
+ num = proc_listallpids(ptr, ptr.size)
146
+
147
+ raise SystemCallError.new('proc_listallpids', FFI.errno) if num == 0
148
+
149
+ pids = ptr.get_array_of_int32(0, num).sort
150
+ array = block_given? ? nil : []
151
+
152
+ pids.each do |lpid|
153
+ next unless pid == lpid if pid
154
+ info = ProcTaskAllInfo.new
155
+
156
+ if proc_pidinfo(lpid, PROC_PIDTASKALLINFO, 0, info, info.size) <= 0
157
+ if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno)
158
+ next # Either we don't have permission, or the pid no longer exists
159
+ else
160
+ raise SystemCallError.new('proc_pidinfo', FFI.errno)
161
+ end
162
+ end
163
+
164
+ struct = ProcTableStruct.new
165
+ get_cmd_args_and_env(lpid, struct) # Pass by reference
166
+
167
+ # Chop the leading xx_ from the FFI struct members for our ruby struct.
168
+ info.members.each do |nested|
169
+ info[nested].members.each do |member|
170
+ temp = member.to_s.split('_')
171
+ sproperty = temp.size > 1 ? temp[1..-1].join('_') : temp.first
172
+ if info[nested][member].is_a?(FFI::StructLayout::CharArray)
173
+ struct[sproperty.to_sym] = info[nested][member].to_s
174
+ else
175
+ struct[sproperty.to_sym] = info[nested][member]
176
+ end
177
+ end
178
+ end
179
+
180
+ struct.freeze
181
+
182
+ if block_given?
183
+ yield struct
184
+ else
185
+ array << struct
186
+ end
187
+ end
188
+
189
+ return nil if array.nil?
190
+ pid ? array.first : array
191
+ end
192
+
193
+ private
194
+
195
+ # Get the command line arguments, as well as the environment settings,
196
+ # for the given PID.
197
+ #
198
+ def self.get_cmd_args_and_env(pid, struct)
199
+ len = FFI::MemoryPointer.new(:size_t)
200
+ mib = FFI::MemoryPointer.new(:int, 3)
201
+
202
+ # Since we may not have access to the process information due
203
+ # to improper privileges, just bail if we see a failure here.
204
+
205
+ mib.write_array_of_int([CTL_KERN, KERN_PROCARGS, pid])
206
+ return if sysctl(mib, 3, nil, len, nil, 0) < 0
207
+
208
+ buf = FFI::MemoryPointer.new(:char, len.read_ulong)
209
+ return if sysctl(mib, 3, buf, len, nil, 0) < 0
210
+
211
+ exe = buf.read_string # Read up to first null, does not include args
212
+ struct[:exe] = exe
213
+
214
+ # Parse the rest of the information out of big, ugly sring
215
+ array = buf.read_bytes(len.read_ulong).split(0.chr)
216
+ array.shift # Delete first exe
217
+ array.delete('') # Delete empty strings
218
+
219
+ cmdline = ''
220
+
221
+ # Anything that doesn't include a '=' sign is a cmdline argument.
222
+ while array[0] && !array[0].include?('=')
223
+ cmdline << ' ' + array.shift
224
+ end
225
+
226
+ struct[:cmdline] = File.basename(cmdline.strip)
227
+
228
+ # Anything remaining at this point is a collect of key=value pairs which
229
+ # we convert into a hash.
230
+ environ = array.inject({}) do |hash, string|
231
+ if string && string.include?('=')
232
+ key, value = string.split('=')
233
+ hash[key] = value
234
+ end
235
+ hash
236
+ end
237
+
238
+ struct[:environ] = environ
239
+ end
240
+ end
241
+ end
@@ -1,6 +1,6 @@
1
1
  module Sys
2
2
  class ProcTable
3
3
  # The version of the sys-proctable library
4
- VERSION = '1.0.0'
4
+ VERSION = '1.1.0'.freeze
5
5
  end
6
6
  end
@@ -2,9 +2,9 @@ require 'rubygems'
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'sys-proctable'
5
- spec.version = '1.0.0'
5
+ spec.version = '1.1.0'
6
6
  spec.author = 'Daniel J. Berger'
7
- spec.license = 'Artistic 2.0'
7
+ spec.license = 'Apache 2.0'
8
8
  spec.email = 'djberg96@gmail.com'
9
9
  spec.homepage = 'http://github.com/djberg96/sys-proctable'
10
10
  spec.platform = Gem::Platform::CURRENT # Probably altered by Rake task
@@ -15,66 +15,77 @@ class TC_ProcTable_All < Test::Unit::TestCase
15
15
  end
16
16
 
17
17
  def setup
18
- @pid = @@windows ? 0 : 1
18
+ @pid = Process.pid
19
19
  end
20
20
 
21
- def test_version
22
- assert_equal('1.0.0', ProcTable::VERSION)
21
+ test "version is set to expected value" do
22
+ assert_equal('1.1.0', ProcTable::VERSION)
23
23
  end
24
24
 
25
- def test_fields
25
+ test "fields basic functionality" do
26
26
  assert_respond_to(ProcTable, :fields)
27
27
  assert_nothing_raised{ ProcTable.fields }
28
+ end
29
+
30
+ test "fields returns expected type" do
28
31
  assert_kind_of(Array, ProcTable.fields)
29
32
  assert_kind_of(String, ProcTable.fields.first)
30
33
  end
31
34
 
32
- def test_ps
35
+ test "ps basic functionality" do
33
36
  assert_respond_to(ProcTable, :ps)
34
37
  assert_nothing_raised{ ProcTable.ps }
35
38
  assert_nothing_raised{ ProcTable.ps{} }
36
39
  end
37
40
 
38
- def test_ps_with_pid
41
+ test "ps accepts an optional pid" do
39
42
  assert_nothing_raised{ ProcTable.ps(0) }
40
43
  end
41
44
 
42
- def test_ps_with_explicit_nil
45
+ test "ps with explicit nil works as expected" do
43
46
  assert_nothing_raised{ ProcTable.ps(nil) }
44
47
  assert_kind_of(Array, ProcTable.ps(nil))
45
48
  end
46
49
 
47
- def test_ps_return_value
50
+ test "ps returns expected results" do
48
51
  assert_kind_of(Array, ProcTable.ps)
49
52
  assert_kind_of(Struct::ProcTableStruct, ProcTable.ps(@pid))
50
- assert_equal(nil, ProcTable.ps(999999999))
51
- assert_equal(nil, ProcTable.ps(999999999){})
52
- assert_equal(nil, ProcTable.ps{})
53
53
  end
54
54
 
55
- def test_ps_returned_struct_is_frozen
55
+ test "ps returns nil if process does not exist" do
56
+ assert_nil(ProcTable.ps(999999999))
57
+ assert_nil(ProcTable.ps(999999999){})
58
+ assert_nil(ProcTable.ps{})
59
+ end
60
+
61
+ test "structs returned by ps are frozen" do
56
62
  assert_true(ProcTable.ps.first.frozen?)
57
63
  end
58
64
 
59
- def test_ps_expected_errors
65
+ test "ps accepts numeric arguments only" do
60
66
  assert_raises(TypeError){ ProcTable.ps('vim') }
67
+ end
68
+
69
+ test "ps accepts a maximum of one argument on Unix platforms" do
61
70
  omit_if(@@windows, 'ArgumentError check skipped on MS Windows')
62
71
  assert_raises(ArgumentError){ ProcTable.ps(0, 'localhost') }
63
72
  end
64
73
 
65
- def test_new_not_allowed
74
+ test "traditional constructor is disabled" do
66
75
  assert_raise(NoMethodError){ Sys::ProcTable.new }
67
76
  end
68
77
 
69
- def test_error_class_defined
78
+ test "custom error class is defined" do
70
79
  assert_not_nil(Sys::ProcTable::Error)
71
80
  assert_kind_of(StandardError, Sys::ProcTable::Error.new)
72
81
  end
73
82
 
74
- def test_from_thread
75
- Thread.new do
76
- Sys::ProcTable.ps
77
- end.value
83
+ test "ps works within a thread" do
84
+ assert_nothing_raised{
85
+ Thread.new do
86
+ Sys::ProcTable.ps
87
+ end.value
88
+ }
78
89
  end
79
90
 
80
91
  def teardown