sys-proctable 0.9.3-universal-solaris → 0.9.4-universal-solaris

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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 493156f7b41233bb2dc91f61f64d668d681369f2
4
+ data.tar.gz: 73c3ea02760e2a1038a3cb79b5bc1bdd8a7843f5
5
+ SHA512:
6
+ metadata.gz: d47a88803966ff31fbd8b3010ea9856dfe35bb424a18ec87b62972860228bc0467aae6ffdb4cfbb33a07ede4a38938022a6cdce5c11aa9d0497c6e9e621a1abd
7
+ data.tar.gz: 36c9b04c0f5668a1bd50aa83dc1bec55f2e4fd92178184703db047d805770e85a1c2d6789a0011c29542963b975beed4b18d5969ae4a5a05bbd586f7e1388a75
data/CHANGES CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.9.4 - 4-Mar-2014
2
+ * Added support for AIX 5.3 or later courtesy of Rick Ohnemus.
3
+ * The Solaris version now uses FFI structs instead of a packed array.
4
+ It solved issues with 64-bit versions of Ruby and it's self-documenting.
5
+ * The FreeBSD version has been converted to use FFI. In addition, additional
6
+ struct members have been added, and members that previously returned nil
7
+ now return meaningful data.
8
+ * Support for NetBSD and OpenBSD has been temporarily dropped. Considering
9
+ that the C code did not build on those platforms anyway, I doubt most of
10
+ you will notice. Patches for those platforms are welcome, but only using FFI.
11
+
1
12
  == 0.9.3 - 17-Mar-2013
2
13
  * Fixed a bug on OSX where a long command string arg could cause
3
14
  a segfault. Thanks go to Nathaniel Bibler for the spot.
data/MANIFEST CHANGED
@@ -3,6 +3,7 @@
3
3
  * Rakefile
4
4
  * README
5
5
  * sys-proctable.gemspec
6
+ * doc/aix.txt
6
7
  * doc/bsd.txt
7
8
  * doc/hpux.txt
8
9
  * doc/linux.txt
@@ -10,20 +11,21 @@
10
11
  * doc/top.txt
11
12
  * doc/windows.txt
12
13
  * example/example_ps.rb
13
- * ext/bsd/extconf.rb
14
- * ext/bsd/sys/proctable.c
15
14
  * ext/darwin/extconf.rb
16
15
  * ext/darwin/sys/proctable.c
17
16
  * ext/hpux/extconf.rb
18
17
  * ext/hpux/sys/proctable.c
19
18
  * lib/sys/top.rb
19
+ * lib/aix/sys/proctable.rb
20
+ * lib/freebsd/sys/proctable.rb
20
21
  * lib/linux/sys/proctable.rb
21
22
  * lib/sunos/sys/proctable.rb
22
23
  * lib/windows/sys/proctable.rb
24
+ * test/test_sys_proctable_aix.rb
23
25
  * test/test_sys_proctable_all.rb
24
26
  * test/test_sys_proctable_darwin.rb
27
+ * test/test_sys_proctable_freebsd.rb
25
28
  * test/test_sys_proctable_hpux.rb
26
- * test/test_sys_proctable_bsd.rb
27
29
  * test/test_sys_proctable_linux.rb
28
30
  * test/test_sys_proctable_sunos.rb
29
31
  * test/test_sys_proctable_windows.rb
data/README CHANGED
@@ -7,7 +7,7 @@
7
7
  == Supported Platforms
8
8
  * Windows 2000 or later
9
9
  * Linux 2.6+
10
- * BSD (various flavors)
10
+ * FreeBSD
11
11
  * Solaris 8+
12
12
  * HP-UX 10+
13
13
  * OS X 10.4+
@@ -20,7 +20,7 @@
20
20
  gem install sys-proctable --platform mswin32 # Windows
21
21
  gem install sys-proctable --platform sunos # Solaris
22
22
  gem install sys-proctable --platform linux # Linux
23
- gem install sys-proctable --platform freebsd # BSD (any flavor)
23
+ gem install sys-proctable --platform freebsd # FreeBSD
24
24
 
25
25
  == Synopsis
26
26
  require 'sys/proctable'
@@ -51,7 +51,7 @@
51
51
  information from a different host. This relies on the WMI service running.
52
52
 
53
53
  == Known Issues
54
- === BSD
54
+ === FreeBSD
55
55
  A kvm interface is used. That means the owner of the process using the
56
56
  sys-proctable library needs to be a member of the kvm group (or root).
57
57
 
@@ -67,13 +67,12 @@
67
67
  Using readdir_r() still won't solve all potential thread safety issues anyway.
68
68
 
69
69
  == Future Plans
70
- Research has indicated that the kvm approach is less favored than a sysctl
71
- approach on BSD variants. I will try to add this interface in a future
72
- release.
70
+ Add support for NetBSD and OpenBSD.
71
+ Convert existing C code to FFI.
73
72
 
74
73
  == Acknowledgements
75
- This library is largely based on the Perl module Proc::ProcessTable by
76
- Dan Urist. Many ideas, as well as large chunks of code, were taken
74
+ This library was originally based on the Perl module Proc::ProcessTable
75
+ by Dan Urist. Many ideas, as well as large chunks of code, were taken
77
76
  from his work. So, a big THANK YOU goes out to Dan Urist.
78
77
 
79
78
  A big thanks also goes out to Mike Hall who was very helpful with ideas,
@@ -105,7 +104,7 @@
105
104
  Artistic 2.0
106
105
 
107
106
  == Copyright
108
- (C) 2003-2012 Daniel J. Berger
107
+ (C) 2003-2014 Daniel J. Berger
109
108
  All Rights Reserved.
110
109
 
111
110
  == Author
data/Rakefile CHANGED
@@ -8,7 +8,7 @@ CLEAN.include(
8
8
  '**/*.core', # Core dump files
9
9
  '**/*.gem', # Gem files
10
10
  '**/*.rbc', # Rubinius
11
- '.rbx', '**/*/.rbx', # Rubinius
11
+ '**/*.rbx', # Rubinius
12
12
  '**/*.o', # C object file
13
13
  '**/*.log', # Ruby extension build log
14
14
  '**/Makefile', # C Makefile
@@ -27,9 +27,6 @@ task :build => [:clean] do
27
27
  end
28
28
 
29
29
  case CONFIG['host_os']
30
- when /bsd/i
31
- dir = 'ext/bsd'
32
- ext = '.so'
33
30
  when /darwin/i
34
31
  dir = 'ext/darwin'
35
32
  ext = '.bundle'
@@ -38,7 +35,7 @@ task :build => [:clean] do
38
35
  ext = '.sl'
39
36
  end
40
37
 
41
- unless CONFIG['host_os'] =~ /win32|mswin|dos|cygwin|mingw|windows|linux|sunos|solaris/i
38
+ if CONFIG['host_os'] =~ /darwin|hpux/i
42
39
  Dir.chdir(dir) do
43
40
  ruby 'extconf.rb'
44
41
  sh 'make'
@@ -61,8 +58,10 @@ task :install => [:build] do
61
58
  file = 'lib/linux/sys/proctable.rb'
62
59
  when /sunos|solaris/i
63
60
  file = 'lib/sunos/sys/proctable.rb'
64
- when /bsd/i
65
- Dir.chdir('ext/bsd'){ sh 'make install' }
61
+ when /aix/i
62
+ file = 'lib/aix/sys/proctable.rb'
63
+ when /freebsd/i
64
+ file = 'lib/freebsd/sys/proctable.rb'
66
65
  when /darwin/i
67
66
  Dir.chdir('ext/darwin'){ sh 'make install' }
68
67
  when /hpux/i
@@ -75,12 +74,12 @@ end
75
74
  desc 'Uninstall the sys-proctable library'
76
75
  task :uninstall do
77
76
  case CONFIG['host_os']
78
- when /win32|mswin|dos|cygwin|mingw|windows|linux|sunos|solaris/i
79
- dir = File.join(CONFIG['sitelibdir'], 'sys')
80
- file = File.join(dir, 'proctable.rb')
81
- else
77
+ when /darwin|hpux/i
82
78
  dir = File.join(CONFIG['sitearchdir'], 'sys')
83
79
  file = File.join(dir, 'proctable.' + CONFIG['DLEXT'])
80
+ else
81
+ dir = File.join(CONFIG['sitelibdir'], 'sys')
82
+ file = File.join(dir, 'proctable.rb')
84
83
  end
85
84
 
86
85
  rm(file)
@@ -111,12 +110,15 @@ Rake::TestTask.new do |t|
111
110
  when /sunos|solaris/i
112
111
  t.test_files = FileList['test/test_sys_proctable_sunos.rb']
113
112
  t.libs << 'lib/sunos'
113
+ when /aix/i
114
+ t.test_files = FileList['test/test_sys_proctable_aix.rb']
115
+ t.libs << 'lib/aix'
116
+ when /freebsd/i
117
+ t.test_files = FileList['test/test_sys_proctable_freebsd.rb']
118
+ t.libs << 'lib/freebsd'
114
119
  when /darwin/i
115
120
  t.libs << 'ext/darwin'
116
121
  t.test_files = FileList['test/test_sys_proctable_darwin.rb']
117
- when /bsd/i
118
- t.libs << 'ext/bsd'
119
- t.test_files = FileList['test/test_sys_proctable_bsd.rb']
120
122
  when /hpux/i
121
123
  t.libs << 'ext/hpux'
122
124
  t.test_files = FileList['test/test_sys_proctable_hpux.rb']
@@ -133,13 +135,12 @@ namespace :gem do
133
135
  # of some bugginess in Rubygems' platform.rb.
134
136
  #
135
137
  case CONFIG['host_os']
136
- when /bsd/i
138
+ when /freebsd/i
137
139
  spec.platform = Gem::Platform.new(['universal', 'freebsd'])
138
- spec.platform.version = nil
139
- spec.files << 'ext/bsd/sys/proctable.c'
140
- spec.extra_rdoc_files << 'ext/bsd/sys/proctable.c'
141
- spec.test_files << 'test/test_sys_proctable_bsd.rb'
142
- spec.extensions = ['ext/bsd/extconf.rb']
140
+ spec.require_paths = ['lib', 'lib/freebsd']
141
+ spec.files += ['lib/freebsd/sys/proctable.rb']
142
+ spec.test_files << 'test/test_sys_proctable_freebsd.rb'
143
+ spec.add_dependency('ffi')
143
144
  when /darwin/i
144
145
  spec.platform = Gem::Platform.new(['universal', 'darwin'])
145
146
  spec.files << 'ext/darwin/sys/proctable.c'
@@ -162,6 +163,11 @@ namespace :gem do
162
163
  spec.require_paths = ['lib', 'lib/sunos']
163
164
  spec.files += ['lib/sunos/sys/proctable.rb']
164
165
  spec.test_files << 'test/test_sys_proctable_sunos.rb'
166
+ when /aix/i
167
+ spec.platform = Gem::Platform.new(['universal', 'aix5'])
168
+ spec.require_paths = ['lib', 'lib/aix']
169
+ spec.files += ['lib/aix/sys/proctable.rb']
170
+ spec.test_files << 'test/test_sys_proctable_aix.rb'
165
171
  when /mswin|win32|dos|cygwin|mingw|windows/i
166
172
  spec.platform = Gem::Platform.new(['universal', 'mingw32'])
167
173
  spec.require_paths = ['lib', 'lib/windows']
@@ -172,7 +178,12 @@ namespace :gem do
172
178
  # https://github.com/rubygems/rubygems/issues/147
173
179
  spec.original_platform = spec.platform
174
180
 
175
- Gem::Builder.new(spec).build
181
+ if Gem::VERSION < "2.0"
182
+ Gem::Builder.new(spec).build
183
+ else
184
+ require 'rubygems/package'
185
+ Gem::Package.build(spec)
186
+ end
176
187
  end
177
188
 
178
189
  desc 'Install the sys-proctable library as a gem'
@@ -3,460 +3,448 @@
3
3
  #
4
4
  # A pure Ruby version of sys-proctable for SunOS 5.8 or later.
5
5
  ########################################################################
6
+ require 'ffi'
6
7
 
7
8
  # The Sys module serves as a namespace only.
8
9
  module Sys
9
10
 
10
- # The ProcTable class encapsulates process table information.
11
- class ProcTable
12
-
13
- class Error < StandardError; end
14
-
15
- # There is no constructor
16
- private_class_method :new
17
-
18
- # The version of the sys-proctable library
19
- VERSION = '0.9.3'
20
-
21
- private
22
-
23
- PRNODEV = -1 # non-existent device
24
-
25
- @fields = [
26
- :flag, # process flags (deprecated)
27
- :nlwp, # number of active lwp's in the process
28
- :pid, # unique process id
29
- :ppid, # process id of parent
30
- :pgid, # pid of session leader
31
- :sid, # session id
32
- :uid, # real user id
33
- :euid, # effective user id
34
- :gid, # real group id
35
- :egid, # effective group id
36
- :addr, # address of the process
37
- :size, # size of process in kbytes
38
- :rssize, # resident set size in kbytes
39
- :ttydev, # tty device (or PRNODEV)
40
- :pctcpu, # % of recent cpu used by all lwp's
41
- :pctmem, # % of system memory used by process
42
- :start, # absolute process start time
43
- :time, # usr + sys cpu time for this process
44
- :ctime, # usr + sys cpu time for reaped children
45
- :fname, # name of the exec'd file
46
- :psargs, # initial characters argument list - same as cmdline
47
- :wstat, # if a zombie, the wait status
48
- :argc, # initial argument count
49
- :argv, # address of initial argument vector
50
- :envp, # address of initial environment vector
51
- :dmodel, # data model of the process
52
- :taskid, # task id
53
- :projid, # project id
54
- :nzomb, # number of zombie lwp's in the process
55
- :poolid, # pool id
56
- :zoneid, # zone id
57
- :contract, # process contract
58
- :lwpid, # lwp id
59
- :wchan, # wait address for sleeping lwp
60
- :stype, # synchronization event type
61
- :state, # numeric lwp state
62
- :sname, # printable character for state
63
- :nice, # nice for cpu usage
64
- :syscall, # system call number (if in syscall)
65
- :pri, # priority
66
- :clname, # scheduling class name
67
- :name, # name of system lwp
68
- :onpro, # processor which last ran thsi lwp
69
- :bindpro, # processor to which lwp is bound
70
- :bindpset, # processor set to which lwp is bound
71
- :count, # number of contributing lwp's
72
- :tstamp, # current time stamp
73
- :create, # process/lwp creation time stamp
74
- :term, # process/lwp termination time stamp
75
- :rtime, # total lwp real (elapsed) time
76
- :utime, # user level cpu time
77
- :stime, # system call cpu time
78
- :ttime, # other system trap cpu time
79
- :tftime, # text page fault sleep time
80
- :dftime, # text page fault sleep time
81
- :kftime, # kernel page fault sleep time
82
- :ltime, # user lock wait sleep time
83
- :slptime, # all other sleep time
84
- :wtime, # wait-cpu (latency) time
85
- :stoptime, # stopped time
86
- :minf, # minor page faults
87
- :majf, # major page faults
88
- :nswap, # swaps
89
- :inblk, # input blocks
90
- :oublk, # output blocks
91
- :msnd, # messages sent
92
- :mrcv, # messages received
93
- :sigs, # signals received
94
- :vctx, # voluntary context switches
95
- :ictx, # involuntary context switches
96
- :sysc, # system calls
97
- :ioch, # chars read and written
98
- :path, # array of symbolic link paths from /proc/<pid>/path
99
- :contracts, # array symbolic link paths from /proc/<pid>/contracts
100
- :fd, # array of used file descriptors
101
- :cmd_args, # array of command line arguments
102
- :environ, # hash of environment associated with the process,
103
- :cmdline # joined cmd_args if present, otherwise psargs
104
- ]
105
-
106
- @psinfo_pack_directive = [
107
- 'i', # pr_flag
108
- 'i', # pr_nlwp
109
- 'i', # pr_pid
110
- 'i', # pr_ppid
111
- 'i', # pr_pgid
112
- 'i', # pr_sid
113
- 'i', # pr_uid
114
- 'i', # pr_euid
115
- 'i', # pr_gid
116
- 'i', # pr_egid
117
- 'L', # pr_addr
118
- 'L', # pr_size
119
- 'L', # pr_rssize
120
- 'L', # pr_pad1
121
- 'i', # pr_ttydev
122
- 'S', # pr_pctcpu
123
- 'S', # pr_pctmem
124
- 'LL', # pr_start
125
- 'LL', # pr_time
126
- 'LL', # pr_ctime
127
- 'A16', # pr_fname[PRFNSZ]
128
- 'A80', # pr_psargs[PRARGSZ]
129
- 'i', # pr_wstat
130
- 'i', # pr_argc
131
- 'L', # pr_argv
132
- 'L', # pr_envp
133
- 'C', # pr_dmodel
134
- 'A3', # pr_pad2[3]
135
- 'i', # pr_taskid
136
- 'i', # pr_projid
137
- 'i', # pr_nzomb
138
- 'i', # pr_poolid
139
- 'i', # pr_zoneid
140
- 'i', # pr_contract
141
- 'i', # pr_filler
142
- # --- LWPSINFO ---
143
- 'i', # pr_flag
144
- 'i', # pr_lwpid
145
- 'L', # pr_addr
146
- 'L', # pr_wchan
147
- 'C', # pr_stype
148
- 'C', # pr_state
149
- 'A', # pr_sname
150
- 'C', # pr_nice
151
- 's', # pr_syscall
152
- 'C', # pr_oldpri
153
- 'C', # pr_cpu
154
- 'i', # pr_pri
155
- 'S', # pr_pctcpu
156
- 'S', # pr_pad
157
- 'LL', # pr_start
158
- 'LL', # pr_time
159
- 'A8', # pr_clname[PRCLSZ]
160
- 'A16', # pr_name[PRFNSZ]
161
- 'i', # pr_onpro
162
- 'i', # pr_bindpro
163
- 'i', # pr_bindpset
164
- ].join
165
-
166
- @prusage_pack_directive = [
167
- 'i', # pr_lwpid
168
- 'i', # pr_count
169
- 'L2', # pr_tstamp
170
- 'L2', # pr_create
171
- 'L2', # pr_term
172
- 'L2', # pr_rtime
173
- 'L2', # pr_utime
174
- 'L2', # pr_stime
175
- 'L2', # pr_ttime
176
- 'L2', # pr_tftime
177
- 'L2', # pr_dftime
178
- 'L2', # pr_kftime
179
- 'L2', # pr_ltime
180
- 'L2', # pr_slptime
181
- 'L2', # pr_wtime
182
- 'L2', # pr_stoptime
183
- 'L12', # pr_filltime
184
- 'L', # pr_minf
185
- 'L', # pr_majf
186
- 'L', # pr_nswap
187
- 'L', # pr_inblk
188
- 'L', # pr_oublk
189
- 'L', # pr_msnd
190
- 'L', # pr_mrcv
191
- 'L', # pr_sigs
192
- 'L', # pr_vctx
193
- 'L', # pr_ictx
194
- 'L', # pr_sysc
195
- 'L', # pr_ioch
196
- ].join
197
-
198
- public
199
-
200
- ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
201
- alias comm fname
202
- end
203
-
204
- # In block form, yields a ProcTableStruct for each process entry that you
205
- # have rights to. This method returns an array of ProcTableStruct's in
206
- # non-block form.
207
- #
208
- # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
209
- # returned, or nil if no process information is found for that +pid+.
210
- #
211
- # Example:
212
- #
213
- # # Iterate over all processes
214
- # ProcTable.ps do |proc_info|
215
- # p proc_info
216
- # end
217
- #
218
- # # Print process table information for only pid 1001
219
- # p ProcTable.ps(1001)
220
- #
221
- def self.ps(pid = nil)
222
- raise TypeError unless pid.is_a?(Fixnum) if pid
223
-
224
- array = block_given? ? nil : []
225
- struct = nil
226
-
227
- Dir.foreach("/proc") do |file|
228
- next if file =~ /\D/ # Skip non-numeric entries under /proc
229
-
230
- # Only return information for a given pid, if provided
231
- if pid
232
- next unless file.to_i == pid
233
- end
234
-
235
- # Skip over any entries we don't have permissions to read
236
- next unless File.readable?("/proc/#{file}/psinfo")
237
-
238
- psinfo = IO.read("/proc/#{file}/psinfo") rescue next
239
-
240
- psinfo_array = psinfo.unpack(@psinfo_pack_directive)
241
-
242
- struct = ProcTableStruct.new
243
-
244
- struct.flag = psinfo_array[0] # pr_flag
245
- struct.nlwp = psinfo_array[1] # pr_nlwp
246
- struct.pid = psinfo_array[2] # pr_pid
247
- struct.ppid = psinfo_array[3] # pr_ppid
248
- struct.pgid = psinfo_array[4] # pr_pgid
249
- struct.sid = psinfo_array[5] # pr_sid
250
- struct.uid = psinfo_array[6] # pr_uid
251
- struct.euid = psinfo_array[7] # pr_euid
252
- struct.gid = psinfo_array[8] # pr_gid
253
- struct.egid = psinfo_array[9] # pr_egid
254
- struct.addr = psinfo_array[10] # pr_addr
255
- struct.size = psinfo_array[11] * 1024 # pr_size (in bytes)
256
- struct.rssize = psinfo_array[12] * 1024 # pr_rssize (in bytes)
257
-
258
- # skip pr_pad1
259
-
260
- struct.ttydev = psinfo_array[14] # pr_ttydev
261
- struct.pctcpu = (psinfo_array[15] * 100).to_f / 0x8000 # pr_pctcpu
262
- struct.pctmem = (psinfo_array[16] * 100).to_f / 0x8000 # pr_pctmem
263
-
264
- struct.start = Time.at(psinfo_array[17]) # pr_start (tv_sec)
265
- struct.time = psinfo_array[19] # pr_time (tv_sec)
266
- struct.ctime = psinfo_array[21] # pr_ctime (tv_sec)
267
-
268
- struct.fname = psinfo_array[23] # pr_fname
269
- struct.psargs = psinfo_array[24] # pr_psargs
270
- struct.wstat = psinfo_array[25] # pr_wstat
271
- struct.argc = psinfo_array[26] # pr_argc
272
- struct.argv = psinfo_array[27] # pr_argv
273
- struct.envp = psinfo_array[28] # pr_envp
274
- struct.dmodel = psinfo_array[29] # pr_dmodel
275
-
276
- # skip pr_pad2
277
-
278
- struct.taskid = psinfo_array[31] # pr_taskid
279
- struct.projid = psinfo_array[32] # pr_projid
280
- struct.nzomb = psinfo_array[33] # pr_nzomb
281
- struct.poolid = psinfo_array[34] # pr_poolid
282
- struct.zoneid = psinfo_array[35] # pr_zoneid
283
- struct.contract = psinfo_array[36] # pr_contract
284
-
285
- # skip pr_filler
286
-
287
- ### LWPSINFO struct info
288
-
289
- # skip pr_flag
290
-
291
- struct.lwpid = psinfo_array[39] # pr_lwpid
292
-
293
- # skip pr_addr
294
-
295
- struct.wchan = psinfo_array[41] # pr_wchan
296
- struct.stype = psinfo_array[42] # pr_stype
297
- struct.state = psinfo_array[43] # pr_state
298
- struct.sname = psinfo_array[44] # pr_sname
299
- struct.nice = psinfo_array[45] # pr_nice
300
- struct.syscall = psinfo_array[46] # pr_syscall
301
-
302
- # skip pr_oldpri
303
- # skip pr_cpu
304
-
305
- struct.pri = psinfo_array[49] # pr_pri
306
-
307
- # skip pr_pctcpu
308
- # skip pr_pad
309
- # skip pr_start
310
- # skip pr_time
311
-
312
- struct.clname = psinfo_array[56] # pr_clname
313
- struct.name = psinfo_array[57] # pr_name
314
- struct.onpro = psinfo_array[58] # pr_onpro
315
- struct.bindpro = psinfo_array[59] # pr_bindpro
316
- struct.bindpset = psinfo_array[60] # pr_bindpset
317
-
318
- # Get the full command line out of /proc/<pid>/as.
319
- begin
320
- File.open("/proc/#{file}/as") do |fd|
321
- fd.sysseek(struct.argv, IO::SEEK_SET)
322
- address = fd.sysread(struct.argc * 4).unpack("L")[0]
323
-
324
- struct.cmd_args = []
325
-
326
- 0.upto(struct.argc - 1){ |i|
327
- fd.sysseek(address, IO::SEEK_SET)
328
- data = fd.sysread(128)[/^[^\0]*/] # Null strip
329
- struct.cmd_args << data
330
- address += data.length + 1 # Add 1 for the space
331
- }
332
-
333
- # Get the environment hash associated with the process.
334
- struct.environ = {}
335
-
336
- fd.sysseek(struct.envp, IO::SEEK_SET)
337
-
338
- env_address = fd.sysread(128).unpack("L")[0]
339
-
340
- # TODO: Optimization potential here.
341
- loop do
342
- fd.sysseek(env_address, IO::SEEK_SET)
343
- data = fd.sysread(1024)[/^[^\0]*/] # Null strip
344
- break if data.empty?
345
- key, value = data.split('=')
346
- struct.environ[key] = value
347
- env_address += data.length + 1 # Add 1 for the space
348
- end
349
- end
350
- rescue Errno::EACCES, Errno::EOVERFLOW, EOFError
351
- # Skip this if we don't have proper permissions, if there's
352
- # no associated environment, or if there's a largefile issue.
353
- rescue Errno::ENOENT
354
- next # The process has terminated. Bail out!
355
- end
356
-
357
- ### struct prusage
358
-
359
- begin
360
- prusage = 0.chr * 512
361
- prusage = IO.read("/proc/#{file}/usage")
362
-
363
- prusage_array = prusage.unpack(@prusage_pack_directive)
364
-
365
- # skip pr_lwpid
366
- struct.count = prusage_array[1]
367
- struct.tstamp = prusage_array[2]
368
- struct.create = prusage_array[4]
369
- struct.term = prusage_array[6]
370
- struct.rtime = prusage_array[8]
371
- struct.utime = prusage_array[10]
372
- struct.stime = prusage_array[12]
373
- struct.ttime = prusage_array[14]
374
- struct.tftime = prusage_array[16]
375
- struct.dftime = prusage_array[18]
376
- struct.kftime = prusage_array[20]
377
- struct.ltime = prusage_array[22]
378
- struct.slptime = prusage_array[24]
379
- struct.wtime = prusage_array[26]
380
- struct.stoptime = prusage_array[28]
381
- # skip filltime
382
- struct.minf = prusage_array[42]
383
- struct.majf = prusage_array[43]
384
- struct.nswap = prusage_array[44]
385
- struct.inblk = prusage_array[45]
386
- struct.oublk = prusage_array[46]
387
- struct.msnd = prusage_array[47]
388
- struct.mrcv = prusage_array[48]
389
- struct.sigs = prusage_array[49]
390
- struct.vctx = prusage_array[50]
391
- struct.ictx = prusage_array[51]
392
- struct.sysc = prusage_array[52]
393
- struct.ioch = prusage_array[53]
394
- rescue Errno::EACCES
395
- # Do nothing if we lack permissions. Just move on.
396
- rescue Errno::ENOENT
397
- next # The process has terminated. Bail out!
398
- end
399
-
400
- # Information from /proc/<pid>/path. This is represented as a hash,
401
- # with the symbolic link name as the key, and the file it links to
402
- # as the value, or nil if it cannot be found.
403
- #--
404
- # Note that cwd information can be gathered from here, too.
405
- struct.path = {}
406
-
407
- Dir["/proc/#{file}/path/*"].each{ |entry|
408
- link = File.readlink(entry) rescue nil
409
- struct.path[File.basename(entry)] = link
11
+ # The ProcTable class encapsulates process table information.
12
+ class ProcTable
13
+ extend FFI::Library
14
+
15
+ class Error < StandardError; end
16
+
17
+ # There is no constructor
18
+ private_class_method :new
19
+
20
+ # The version of the sys-proctable library
21
+ VERSION = '0.9.4'
22
+
23
+ private
24
+
25
+ class Timeval < FFI::Struct
26
+ layout(:tv_sec, :time_t, :tv_usec, :time_t)
27
+ end
28
+
29
+ class LWPSInfo < FFI::Struct
30
+ layout(
31
+ :pr_flag, :int,
32
+ :pr_lwpid, :id_t,
33
+ :pr_addr, :uintptr_t,
34
+ :pr_wchan, :uintptr_t,
35
+ :pr_stype, :char,
36
+ :pr_state, :char,
37
+ :pr_sname, :char,
38
+ :pr_nice, :char,
39
+ :pr_syscall, :short,
40
+ :pr_oldpri, :char,
41
+ :pr_cpu, :char,
42
+ :pr_pri, :int,
43
+ :pr_pctcpu, :ushort_t,
44
+ :pr_pad, :ushort_t,
45
+ :pr_start, Timeval,
46
+ :pr_time, Timeval,
47
+ :pr_clname, [:char, 8],
48
+ :pr_name, [:char, 16],
49
+ :pr_onpro, :int,
50
+ :pr_bindpro, :int,
51
+ :pr_bindpset, :int,
52
+ :pr_filler, [:int, 5]
53
+ )
54
+ end
55
+
56
+ class PSInfo < FFI::Struct
57
+ layout(
58
+ :pr_flag, :int,
59
+ :pr_nlwp, :int,
60
+ :pr_pid, :pid_t,
61
+ :pr_ppid, :pid_t,
62
+ :pr_pgid, :pid_t,
63
+ :pr_sid, :pid_t,
64
+ :pr_uid, :uid_t,
65
+ :pr_euid, :uid_t,
66
+ :pr_gid, :gid_t,
67
+ :pr_egid, :gid_t,
68
+ :pr_addr, :uintptr_t,
69
+ :pr_size, :size_t,
70
+ :pr_rssize, :size_t,
71
+ :pr_pad1, :size_t,
72
+ :pr_ttydev, :dev_t,
73
+ :pr_pctcpu, :ushort_t,
74
+ :pr_pctmem, :ushort_t,
75
+ :pr_start, Timeval,
76
+ :pr_time, Timeval,
77
+ :pr_ctime, Timeval,
78
+ :pr_fname, [:char, 16],
79
+ :pr_psargs, [:char, 80],
80
+ :pr_wstat, :int,
81
+ :pr_argc, :int,
82
+ :pr_argv, :uintptr_t,
83
+ :pr_envp, :uintptr_t,
84
+ :pr_dmodel, :char,
85
+ :pr_pad2, [:char, 3],
86
+ :pr_taskid, :taskid_t,
87
+ :pr_projid, :projid_t,
88
+ :pr_nzomb, :int,
89
+ :pr_poolid, :poolid_t,
90
+ :pr_zoneid, :zoneid_t,
91
+ :pr_contract, :id_t,
92
+ :pr_filler, [:int, 1],
93
+ :pr_lwp, LWPSInfo
94
+ )
95
+ end
96
+
97
+ class PRUsage < FFI::Struct
98
+ layout(
99
+ :pr_lwpid, :id_t,
100
+ :pr_count, :int,
101
+ :pr_tstamp, Timeval,
102
+ :pr_create, Timeval,
103
+ :pr_term, Timeval,
104
+ :pr_rtime, Timeval,
105
+ :pr_utime, Timeval,
106
+ :pr_stime, Timeval,
107
+ :pr_ttime, Timeval,
108
+ :pr_tftime, Timeval,
109
+ :pr_dftime, Timeval,
110
+ :pr_kftime, Timeval,
111
+ :pr_ltime, Timeval,
112
+ :pr_slptime, Timeval,
113
+ :pr_wtime, Timeval,
114
+ :pr_stoptime, Timeval,
115
+ :pr_filetime, [Timeval,6],
116
+ :pr_minf, :ulong_t,
117
+ :pr_majf, :ulong_t,
118
+ :pr_nswap, :ulong_t,
119
+ :pr_inblk, :ulong_t,
120
+ :pr_oublk, :ulong_t,
121
+ :pr_msnd, :ulong_t,
122
+ :pr_mrcv, :ulong_t,
123
+ :pr_sigs, :ulong_t,
124
+ :pr_vctx, :ulong_t,
125
+ :pr_ictx, :ulong_t,
126
+ :pr_sysc, :ulong_t,
127
+ :pr_ioch, :ulong_t,
128
+ :filler, [:ulong_t, 10]
129
+ )
130
+ end
131
+
132
+ PRNODEV = (1<<FFI::Platform::ADDRESS_SIZE)-1
133
+
134
+ @fields = [
135
+ :flag, # process flags (deprecated)
136
+ :nlwp, # number of active lwp's in the process
137
+ :pid, # unique process id
138
+ :ppid, # process id of parent
139
+ :pgid, # pid of session leader
140
+ :sid, # session id
141
+ :uid, # real user id
142
+ :euid, # effective user id
143
+ :gid, # real group id
144
+ :egid, # effective group id
145
+ :addr, # address of the process
146
+ :size, # size of process in kbytes
147
+ :rssize, # resident set size in kbytes
148
+ :ttydev, # tty device (or PRNODEV)
149
+ :pctcpu, # % of recent cpu used by all lwp's
150
+ :pctmem, # % of system memory used by process
151
+ :start, # absolute process start time
152
+ :time, # usr + sys cpu time for this process
153
+ :ctime, # usr + sys cpu time for reaped children
154
+ :fname, # name of the exec'd file
155
+ :psargs, # initial characters argument list - same as cmdline
156
+ :wstat, # if a zombie, the wait status
157
+ :argc, # initial argument count
158
+ :argv, # address of initial argument vector
159
+ :envp, # address of initial environment vector
160
+ :dmodel, # data model of the process
161
+ :taskid, # task id
162
+ :projid, # project id
163
+ :nzomb, # number of zombie lwp's in the process
164
+ :poolid, # pool id
165
+ :zoneid, # zone id
166
+ :contract, # process contract
167
+ :lwpid, # lwp id
168
+ :wchan, # wait address for sleeping lwp
169
+ :stype, # synchronization event type
170
+ :state, # numeric lwp state
171
+ :sname, # printable character for state
172
+ :nice, # nice for cpu usage
173
+ :syscall, # system call number (if in syscall)
174
+ :pri, # priority
175
+ :clname, # scheduling class name
176
+ :name, # name of system lwp
177
+ :onpro, # processor which last ran thsi lwp
178
+ :bindpro, # processor to which lwp is bound
179
+ :bindpset, # processor set to which lwp is bound
180
+ :count, # number of contributing lwp's
181
+ :tstamp, # current time stamp
182
+ :create, # process/lwp creation time stamp
183
+ :term, # process/lwp termination time stamp
184
+ :rtime, # total lwp real (elapsed) time
185
+ :utime, # user level cpu time
186
+ :stime, # system call cpu time
187
+ :ttime, # other system trap cpu time
188
+ :tftime, # text page fault sleep time
189
+ :dftime, # text page fault sleep time
190
+ :kftime, # kernel page fault sleep time
191
+ :ltime, # user lock wait sleep time
192
+ :slptime, # all other sleep time
193
+ :wtime, # wait-cpu (latency) time
194
+ :stoptime, # stopped time
195
+ :minf, # minor page faults
196
+ :majf, # major page faults
197
+ :nswap, # swaps
198
+ :inblk, # input blocks
199
+ :oublk, # output blocks
200
+ :msnd, # messages sent
201
+ :mrcv, # messages received
202
+ :sigs, # signals received
203
+ :vctx, # voluntary context switches
204
+ :ictx, # involuntary context switches
205
+ :sysc, # system calls
206
+ :ioch, # chars read and written
207
+ :path, # array of symbolic link paths from /proc/<pid>/path
208
+ :contracts, # array symbolic link paths from /proc/<pid>/contracts
209
+ :fd, # array of used file descriptors
210
+ :cmd_args, # array of command line arguments
211
+ :environ, # hash of environment associated with the process,
212
+ :cmdline # joined cmd_args if present, otherwise psargs
213
+ ]
214
+
215
+ public
216
+
217
+ ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
218
+ alias comm fname
219
+ end
220
+
221
+ # In block form, yields a ProcTableStruct for each process entry that you
222
+ # have rights to. This method returns an array of ProcTableStruct's in
223
+ # non-block form.
224
+ #
225
+ # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
226
+ # returned, or nil if no process information is found for that +pid+.
227
+ #
228
+ # Example:
229
+ #
230
+ # # Iterate over all processes
231
+ # ProcTable.ps do |proc_info|
232
+ # p proc_info
233
+ # end
234
+ #
235
+ # # Print process table information for only pid 1001
236
+ # p ProcTable.ps(1001)
237
+ #
238
+ def self.ps(pid = nil)
239
+ raise TypeError unless pid.is_a?(Fixnum) if pid
240
+
241
+ array = block_given? ? nil : []
242
+ struct = nil
243
+
244
+ Dir.foreach("/proc") do |file|
245
+ next if file =~ /\D/ # Skip non-numeric entries under /proc
246
+
247
+ # Only return information for a given pid, if provided
248
+ next unless file.to_i == pid if pid
249
+
250
+ # Skip over any entries we don't have permissions to read
251
+ next unless File.readable?("/proc/#{file}/psinfo")
252
+
253
+ data = IO.read("/proc/#{file}/psinfo") rescue next
254
+ psinfo = PSInfo.new(FFI::MemoryPointer.from_string(data))
255
+
256
+ struct = ProcTableStruct.new
257
+
258
+ struct.flag = psinfo[:pr_flag]
259
+ struct.nlwp = psinfo[:pr_nlwp]
260
+ struct.pid = psinfo[:pr_pid]
261
+ struct.ppid = psinfo[:pr_ppid]
262
+ struct.pgid = psinfo[:pr_pgid]
263
+ struct.sid = psinfo[:pr_sid]
264
+ struct.uid = psinfo[:pr_uid]
265
+ struct.euid = psinfo[:pr_euid]
266
+ struct.gid = psinfo[:pr_gid]
267
+ struct.egid = psinfo[:pr_egid]
268
+ struct.addr = psinfo[:pr_addr]
269
+ struct.size = psinfo[:pr_size] * 1024 # bytes
270
+ struct.rssize = psinfo[:pr_rssize] * 1024 # bytes
271
+ struct.ttydev = psinfo[:pr_ttydev] == PRNODEV ? -1 : psinfo[:pr_ttydev]
272
+ struct.pctcpu = (psinfo[:pr_pctcpu] * 100).to_f / 0x8000
273
+ struct.pctmem = (psinfo[:pr_pctmem] * 100).to_f / 0x8000
274
+
275
+ struct.start = Time.at(psinfo[:pr_start][:tv_sec])
276
+ struct.time = psinfo[:pr_time][:tv_sec]
277
+ struct.ctime = psinfo[:pr_ctime][:tv_sec]
278
+
279
+ struct.fname = psinfo[:pr_fname].to_s
280
+ struct.psargs = psinfo[:pr_psargs].to_s
281
+ struct.wstat = psinfo[:pr_wstat]
282
+ struct.argc = psinfo[:pr_argc]
283
+ struct.argv = psinfo[:pr_argv]
284
+ struct.envp = psinfo[:pr_envp]
285
+ struct.dmodel = psinfo[:pr_dmodel]
286
+
287
+ struct.taskid = psinfo[:pr_taskid]
288
+ struct.projid = psinfo[:pr_projid]
289
+ struct.nzomb = psinfo[:pr_nzomb]
290
+ struct.poolid = psinfo[:pr_poolid]
291
+ struct.zoneid = psinfo[:pr_zoneid]
292
+ struct.contract = psinfo[:pr_contract]
293
+
294
+ ### LWPSINFO struct info
295
+
296
+ struct.lwpid = psinfo[:pr_lwp][:pr_lwpid]
297
+ struct.wchan = psinfo[:pr_lwp][:pr_wchan]
298
+ struct.stype = psinfo[:pr_lwp][:pr_stype]
299
+ struct.state = psinfo[:pr_lwp][:pr_state]
300
+ struct.sname = psinfo[:pr_lwp][:pr_sname].chr
301
+ struct.nice = psinfo[:pr_lwp][:pr_nice]
302
+ struct.syscall = psinfo[:pr_lwp][:pr_syscall]
303
+ struct.pri = psinfo[:pr_lwp][:pr_pri]
304
+ struct.clname = psinfo[:pr_lwp][:pr_clname].to_s
305
+ struct.name = psinfo[:pr_lwp][:pr_name].to_s
306
+ struct.onpro = psinfo[:pr_lwp][:pr_onpro]
307
+ struct.bindpro = psinfo[:pr_lwp][:pr_bindpro]
308
+ struct.bindpset = psinfo[:pr_lwp][:pr_bindpset]
309
+
310
+ # Get the full command line out of /proc/<pid>/as.
311
+ begin
312
+ File.open("/proc/#{file}/as") do |fd|
313
+ fd.sysseek(struct.argv, IO::SEEK_SET)
314
+ address = fd.sysread(struct.argc * 4).unpack("L")[0]
315
+
316
+ struct.cmd_args = []
317
+
318
+ 0.upto(struct.argc - 1){ |i|
319
+ fd.sysseek(address, IO::SEEK_SET)
320
+ data = fd.sysread(128)[/^[^\0]*/] # Null strip
321
+ struct.cmd_args << data
322
+ address += data.length + 1 # Add 1 for the space
410
323
  }
411
324
 
412
- # Information from /proc/<pid>/contracts. This is represented as
413
- # a hash, with the symbolic link name as the key, and the file
414
- # it links to as the value.
415
- struct.contracts = {}
325
+ # Get the environment hash associated with the process.
326
+ struct.environ = {}
416
327
 
417
- Dir["/proc/#{file}/contracts/*"].each{ |entry|
418
- link = File.readlink(entry) rescue nil
419
- struct.contracts[File.basename(entry)] = link
420
- }
328
+ fd.sysseek(struct.envp, IO::SEEK_SET)
421
329
 
422
- # Information from /proc/<pid>/fd. This returns an array of
423
- # numeric file descriptors used by the process.
424
- struct.fd = Dir["/proc/#{file}/fd/*"].map{ |f| File.basename(f).to_i }
425
-
426
- # Use the cmd_args as the cmdline if available. Otherwise use
427
- # the psargs. This struct member is provided to provide a measure
428
- # of consistency with the other platform implementations.
429
- if struct.cmd_args && struct.cmd_args.length > 0
430
- struct.cmdline = struct.cmd_args.join(' ')
431
- else
432
- struct.cmdline = struct.psargs
433
- end
434
-
435
- # This is read-only data
436
- struct.freeze
330
+ env_address = fd.sysread(128).unpack("L")[0]
437
331
 
438
- if block_given?
439
- yield struct
440
- else
441
- array << struct
332
+ # TODO: Optimization potential here.
333
+ loop do
334
+ fd.sysseek(env_address, IO::SEEK_SET)
335
+ data = fd.sysread(1024)[/^[^\0]*/] # Null strip
336
+ break if data.empty?
337
+ key, value = data.split('=')
338
+ struct.environ[key] = value
339
+ env_address += data.length + 1 # Add 1 for the space
442
340
  end
443
- end
444
-
445
- pid ? struct : array
341
+ end
342
+ rescue Errno::EACCES, Errno::EOVERFLOW, EOFError, RangeError
343
+ # Skip this if we don't have proper permissions, if there's
344
+ # no associated environment, or if there's a largefile issue.
345
+ rescue Errno::ENOENT
346
+ next # The process has terminated. Bail out!
347
+ end
348
+
349
+ ### struct prusage
350
+
351
+ begin
352
+ data = IO.read("/proc/#{file}/usage")
353
+ prusage = PRUsage.new(FFI::MemoryPointer.from_string(data))
354
+
355
+ struct.count = prusage[:pr_count]
356
+ struct.tstamp = prusage[:pr_tstamp][:tv_sec]
357
+ struct.create = prusage[:pr_create][:tv_sec]
358
+ struct.term = prusage[:pr_term][:tv_sec]
359
+ struct.rtime = prusage[:pr_rtime][:tv_sec]
360
+ struct.utime = prusage[:pr_utime][:tv_sec]
361
+ struct.stime = prusage[:pr_stime][:tv_sec]
362
+ struct.ttime = prusage[:pr_ttime][:tv_sec]
363
+ struct.tftime = prusage[:pr_tftime][:tv_sec]
364
+ struct.dftime = prusage[:pr_dftime][:tv_sec]
365
+ struct.kftime = prusage[:pr_kftime][:tv_sec]
366
+ struct.ltime = prusage[:pr_ltime][:tv_sec]
367
+ struct.slptime = prusage[:pr_slptime][:tv_sec]
368
+ struct.wtime = prusage[:pr_wtime][:tv_sec]
369
+ struct.stoptime = prusage[:pr_stoptime][:tv_sec]
370
+ struct.minf = prusage[:pr_minf]
371
+ struct.majf = prusage[:pr_majf]
372
+ struct.nswap = prusage[:pr_nswap]
373
+ struct.inblk = prusage[:pr_inblk]
374
+ struct.oublk = prusage[:pr_oublk]
375
+ struct.msnd = prusage[:pr_msnd]
376
+ struct.mrcv = prusage[:pr_mrcv]
377
+ struct.sigs = prusage[:pr_sigs]
378
+ struct.vctx = prusage[:pr_vctx]
379
+ struct.ictx = prusage[:pr_ictx]
380
+ struct.sysc = prusage[:pr_sysc]
381
+ struct.ioch = prusage[:pr_ioch]
382
+ rescue Errno::EACCES
383
+ # Do nothing if we lack permissions. Just move on.
384
+ rescue Errno::ENOENT
385
+ next # The process has terminated. Bail out!
386
+ end
387
+
388
+ # Information from /proc/<pid>/path. This is represented as a hash,
389
+ # with the symbolic link name as the key, and the file it links to
390
+ # as the value, or nil if it cannot be found.
391
+ #--
392
+ # Note that cwd information can be gathered from here, too.
393
+ struct.path = {}
394
+
395
+ Dir["/proc/#{file}/path/*"].each{ |entry|
396
+ link = File.readlink(entry) rescue nil
397
+ struct.path[File.basename(entry)] = link
398
+ }
399
+
400
+ # Information from /proc/<pid>/contracts. This is represented as
401
+ # a hash, with the symbolic link name as the key, and the file
402
+ # it links to as the value.
403
+ struct.contracts = {}
404
+
405
+ Dir["/proc/#{file}/contracts/*"].each{ |entry|
406
+ link = File.readlink(entry) rescue nil
407
+ struct.contracts[File.basename(entry)] = link
408
+ }
409
+
410
+ # Information from /proc/<pid>/fd. This returns an array of
411
+ # numeric file descriptors used by the process.
412
+ struct.fd = Dir["/proc/#{file}/fd/*"].map{ |f| File.basename(f).to_i }
413
+
414
+ # Use the cmd_args as the cmdline if available. Otherwise use
415
+ # the psargs. This struct member is provided to provide a measure
416
+ # of consistency with the other platform implementations.
417
+ if struct.cmd_args && struct.cmd_args.length > 0
418
+ struct.cmdline = struct.cmd_args.join(' ')
419
+ else
420
+ struct.cmdline = struct.psargs
421
+ end
422
+
423
+ # This is read-only data
424
+ struct.freeze
425
+
426
+ if block_given?
427
+ yield struct
428
+ else
429
+ array << struct
430
+ end
446
431
  end
447
432
 
448
- # Returns an array of fields that each ProcTableStruct will contain. This
449
- # may be useful if you want to know in advance what fields are available
450
- # without having to perform at least one read of the /proc table.
451
- #
452
- # Example:
453
- #
454
- # Sys::ProcTable.fields.each{ |field|
455
- # puts "Field: #{field}"
456
- # }
457
- #
458
- def self.fields
459
- @fields.map{ |f| f.to_s }
460
- end
461
- end
433
+ pid ? struct : array
434
+ end
435
+
436
+ # Returns an array of fields that each ProcTableStruct will contain. This
437
+ # may be useful if you want to know in advance what fields are available
438
+ # without having to perform at least one read of the /proc table.
439
+ #
440
+ # Example:
441
+ #
442
+ # Sys::ProcTable.fields.each{ |field|
443
+ # puts "Field: #{field}"
444
+ # }
445
+ #
446
+ def self.fields
447
+ @fields.map{ |f| f.to_s }
448
+ end
449
+ end
462
450
  end