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

Sign up to get free protection for your applications and to get access to all the features.
@@ -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