Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add AIX support.

  • Loading branch information...
commit 21805ea9c4a46a05f722c82b0da961002561b3da 2 parents e44be8a + bc5b720
@djberg96 authored
View
3  MANIFEST
@@ -3,6 +3,7 @@
* Rakefile
* README
* sys-proctable.gemspec
+* doc/aix.txt
* doc/bsd.txt
* doc/hpux.txt
* doc/linux.txt
@@ -17,9 +18,11 @@
* ext/hpux/extconf.rb
* ext/hpux/sys/proctable.c
* lib/sys/top.rb
+* lib/aix/sys/proctable.rb
* lib/linux/sys/proctable.rb
* lib/sunos/sys/proctable.rb
* lib/windows/sys/proctable.rb
+* test/test_sys_proctable_aix.rb
* test/test_sys_proctable_all.rb
* test/test_sys_proctable_darwin.rb
* test/test_sys_proctable_hpux.rb
View
12 Rakefile
@@ -38,7 +38,7 @@ task :build => [:clean] do
ext = '.sl'
end
- unless CONFIG['host_os'] =~ /win32|mswin|dos|cygwin|mingw|windows|linux|sunos|solaris/i
+ unless CONFIG['host_os'] =~ /win32|mswin|dos|cygwin|mingw|windows|linux|sunos|solaris|aix/i
Dir.chdir(dir) do
ruby 'extconf.rb'
sh 'make'
@@ -61,6 +61,8 @@ task :install => [:build] do
file = 'lib/linux/sys/proctable.rb'
when /sunos|solaris/i
file = 'lib/sunos/sys/proctable.rb'
+ when /aix/i
+ file = 'lib/aix/sys/proctable.rb'
when /bsd/i
Dir.chdir('ext/bsd'){ sh 'make install' }
when /darwin/i
@@ -111,6 +113,9 @@ Rake::TestTask.new do |t|
when /sunos|solaris/i
t.test_files = FileList['test/test_sys_proctable_sunos.rb']
t.libs << 'lib/sunos'
+ when /aix/i
+ t.test_files = FileList['test/test_sys_proctable_aix.rb']
+ t.libs << 'lib/aix'
when /darwin/i
t.libs << 'ext/darwin'
t.test_files = FileList['test/test_sys_proctable_darwin.rb']
@@ -162,6 +167,11 @@ namespace :gem do
spec.require_paths = ['lib', 'lib/sunos']
spec.files += ['lib/sunos/sys/proctable.rb']
spec.test_files << 'test/test_sys_proctable_sunos.rb'
+ when /aix/i
+ spec.platform = Gem::Platform.new(['universal', 'aix5'])
+ spec.require_paths = ['lib', 'lib/aix']
+ spec.files += ['lib/aix/sys/proctable.rb']
+ spec.test_files << 'test/test_sys_proctable_aix.rb'
when /mswin|win32|dos|cygwin|mingw|windows/i
spec.platform = Gem::Platform.new(['universal', 'mingw32'])
spec.require_paths = ['lib', 'lib/windows']
View
86 doc/aix.txt
@@ -0,0 +1,86 @@
+= Description
+ A Ruby interface for gathering process table information. This is a pure
+ Ruby implementation that unpacks data out of your /proc filesystem.
+
+= Synopsis
+ require 'sys/proctable'
+ include Sys
+
+ # Everything
+ ProcTable.ps{ |p|
+ puts p.pid.to_s
+ puts p.comm
+ ...
+ }
+
+ or
+
+ # Just one process
+ p = ProcTable.ps(2123)
+ puts p.pid.to_s
+ puts p.comm
+ ...
+
+ or
+
+ # Return the results as an array of ProcTableStructs
+ a = ProcTable.ps()
+ a.each do |p|
+ puts a.pid
+ ...
+ end
+
+= Constants
+VERSION
+ Returns the current version number for this library (as a string).
+
+= Class Methods
+ProcTable.fields
+ Returns an array of fields available on the current OS.
+
+ProcTable.ps(pid=nil)
+ProcTable.ps { |s| ... }
+ If no pid's or processes are included as arguments, in block form it
+ returns a struct of type ProcTableStruct for every process in the proc
+ table. Otherwise it returns an array of ProcTableStruct's.
+
+ If a process id is provided, a single ProcTable struct is returned, or
+ nil if the pid is not found.
+
+= Exception Classes
+ProcTable::Error < StandardError
+ Raised if the /proc field is unreadable and/or unmounted.
+
+= Supported fields
+ You can view the supported fields with the "fields()" class method.
+
+= Future Plans
+ Have the flags fields return meaningful values.
+
+= Notes
+ The "comm" field isn't really part of the psinfo struct. It is just a copy
+ (i.e. is identical to) the "fname" field. It exists to provide a degree
+ of consistency between all of the platforms.
+
+= Known Bugs
+ None known. Please log any bugs on the project page at
+ http://www.rubyforge.org/projects/sysutils
+
+= License
+ Artistic 2.0
+
+= Copyright
+ (C) 2003-2014 Daniel J. Berger
+ All Rights Reserved
+
+= Warranty
+ This package is provided "as is" and without any express or
+ implied warranties, including, without limitation, the implied
+ warranties of merchantability and fitness for a particular purpose.
+
+= Authors
+ Rick Ohnemus
+ Daniel J. Berger
+
+= See Also
+ ps, proc
View
458 lib/aix/sys/proctable.rb
@@ -0,0 +1,458 @@
+#######################################################################
+# proctable.rb
+#
+# A pure Ruby version of sys-proctable for AIX 5.3 or later.
+########################################################################
+
+# The Sys module serves as a namespace only.
+module Sys
+
+ # The ProcTable class encapsulates process table information.
+ class ProcTable
+
+ class Error < StandardError; end
+
+ # There is no constructor
+ private_class_method :new
+
+ # The version of the sys-proctable library
+ VERSION = '0.9.1'
+
+ private
+
+ @fields = [
+ # --- psinfo_t ---
+ :flag, # process flags from proc struct p_flag
+ :flag2, # process flags from proc struct p_flag2
+ :nlwp, # number of threads in process
+ #:pad1, # reserved for future use
+ :uid, # real user id
+ :euid, # effective user id
+ :gid, # real group id
+ :egid, # effective group id
+ :pid, # unique process id
+ :ppid, # process id of parent
+ :pgid, # pid of process group leader
+ :sid, # session id
+ :ttydev, # controlling tty device (device #)
+ :s_ttydev, # controlling tty device name or '-'
+ :addr, # internal address of proc struct
+ :size, # process image size in KB (1024) units
+ :rssize, # resident set size in KB (1024) units
+ :start, # process start time, time since epoch
+ :time, # usr+sys cpu time for this process
+ :cid, # corral id
+ #:pad2, # reserved for future use
+ :argc, # initial argument count
+ :argv, # address of initial argument vector in user process
+ :envp, # address of initial environment vector in user process
+ :fname, # last component of exec()ed pathname
+ :psargs, # initial characters of arg list
+ #:pad, # reserved for future use
+
+ # --- lwpsinfo_t ---
+ :lwpid, # thread id
+ #:addr, # internal address of thread
+ :wchan, # wait addr for sleeping thread
+ #:flag, # thread flags
+ :wtype, # type of thread wait
+ :state, # thread state
+ :sname, # printable thread state character
+ :nice, # nice for cpu usage
+ :pri, # priority, high value = high priority
+ :policy, # scheduling policy
+ :clname, # printable scheduling policy string
+ :onpro, # processor on which thread last ran
+ :bindpro, # processor to which thread is bound
+ :ptid, # pthread id
+ #:pad1, # reserved for future use
+ #:pad, # reserved for future use
+
+ # --- prmap_t ---
+ :map, # array of prmap_t structures
+
+ # --- lwp ---
+ #:lwp # array of lwp information
+
+ # other...
+ :fd, # array of used file descriptors
+ :cmd_args, # array of command line arguments
+ :environ, # hash of environment associated with the process
+ :cmdline, # joined cmd_args if present, otherwise psargs
+ :cwd, # current working directory
+ ]
+
+ @psinfo_pack_directive = [
+ 'L', # pr_flag
+ 'L', # pr_flag2
+ 'L', # pr_nlwp
+ 'L', # pr__pad1
+ 'Q', # pr_uid
+ 'Q', # pr_euid
+ 'Q', # pr_gid
+ 'Q', # pr_egid
+ 'Q', # pr_pid
+ 'Q', # pr_ppid
+ 'Q', # pr_pgid
+ 'Q', # pr_sid
+ 'Q', # pr_ttydev
+ 'Q', # pr_addr
+ 'Q', # pr_size
+ 'Q', # pr_rssize
+ 'QlL', # pr_start
+ 'QlL', # pr_time
+ 'S', # pr_cid
+ 'S', # pr__pad2
+ 'L', # pr_argc
+ 'Q', # pr_argv
+ 'Q', # pr_envp
+ 'A16', # pr_fname[PRFNSZ]
+ 'A80', # pr_psargs[PRARGSZ]
+ 'Q8', # pr__pad[8]
+ # --- lwpsinfo_t --- pr_lwp
+ 'Q', # pr_lwpid
+ 'Q', # pr_addr
+ 'Q', # pr_wchan
+ 'L', # pr_flag
+ 'C', # pr_wtype
+ 'c', # pr_state
+ 'A', # pr_sname
+ 'C', # pr_nice
+ 'l', # pr_pri
+ 'L', # pr_policy
+ 'A8', # pr_clname
+ 'l', # pr_onpro
+ 'l', # pr_bindpro
+ 'L', # pr_ptid
+ 'L', # pr__pad1
+ 'Q7' # pr__pad[7]
+ ].join
+
+ # --- prmap_t ---
+ @map_fields = [
+ :size,
+ :vaddr,
+ :mapname,
+ :off,
+ :mflags,
+ :s_mflags,
+ :pathoff,
+ :alias,
+ :gp,
+ #:pad,
+ :path,
+ ]
+
+ @prmap_pack_directive = [
+ 'Q', # pr_size
+ 'Q', # pr_vaddr
+ 'A64', # pr_mapname[PRMAPSZ]
+ 'Q', # pr_off
+ 'L', # pr_mflags
+ 'L', # pr_pathoff
+ 'Q', # pr_alias
+ 'Q', # pr_gp
+ 'Q8', # pr__pad[8]
+ ].join
+
+ # prmap_t pr_mflags
+
+ PR_MFLAGS =
+ [
+ [ 0x80000000, 'main' ], # MA_MAINEXEC - main executable
+ [ 0x40000000, 'kernel' ], # MA_KERNTEXT - kernel text
+ [ 0x00000004, 'read' ], # MA_READ - readable
+ [ 0x00000002, 'write' ], # MA_WRITE - writable
+ [ 0x00000001, 'exec' ], # MA_EXEC - executable
+ [ 0x00000008, 'shared' ], # MA_SHARED - shared memory region
+ [ 0x00000010, 'heap' ], # MA_BREAK - heap -- grown by brk
+ [ 0x00000020, 'stack' ], # MA_STACK - stack -- grows on stack faults
+ ]
+
+ @devs = {}
+
+ Dir['/dev/**/*'].map do |filename|
+ begin
+ rdev = File.stat(filename).rdev
+ rescue
+ next
+ end
+
+ @devs[rdev] = filename[5..-1] if rdev.nonzero?
+ end
+
+ public
+
+ ProcTableStruct = Struct.new("ProcTableStruct", *@fields) do
+ alias comm fname
+ end
+
+ ProcTableMapStruct = Struct.new("ProcTableMapStruct", *@map_fields)
+
+ # In block form, yields a ProcTableStruct for each process entry that you
+ # have rights to. This method returns an array of ProcTableStruct's in
+ # non-block form.
+ #
+ # If a +pid+ is provided, then only a single ProcTableStruct is yielded or
+ # returned, or nil if no process information is found for that +pid+.
+ #
+ # Example:
+ #
+ # # Iterate over all processes
+ # ProcTable.ps do |proc_info|
+ # p proc_info
+ # end
+ #
+ # # Print process table information for only pid 1001
+ # p ProcTable.ps(1001)
+ #
+ def self.ps(pid = nil)
+ raise TypeError unless (pid.is_a?(Fixnum) || pid.is_a?(Bignum)) if pid
+
+ array = block_given? ? nil : []
+ struct = nil
+
+ Dir.foreach("/proc") do |file|
+ next if file =~ /\D/ # Skip non-numeric entries under /proc
+
+ # Only return information for a given pid, if provided
+ if pid
+ next unless file.to_i == pid
+ end
+
+ # Skip over any entries we don't have permissions to read
+ next unless File.readable?("/proc/#{file}/psinfo")
+
+ psinfo = IO.read("/proc/#{file}/psinfo") rescue next
+
+ psinfo_array = psinfo.unpack(@psinfo_pack_directive)
+
+ struct = ProcTableStruct.new
+
+ struct.flag = psinfo_array[0] # pr_flag
+ struct.flag2 = psinfo_array[1] # pr_flag2
+ struct.nlwp = psinfo_array[2] # pr_nlwp
+ # pr__pad1
+ struct.uid = psinfo_array[4] # pr_uid
+ struct.euid = psinfo_array[5] # pr_euid
+ struct.gid = psinfo_array[6] # pr_gid
+ struct.egid = psinfo_array[7] # pr_egid
+ struct.pid = psinfo_array[8] # pr_pid
+ struct.ppid = psinfo_array[9] # pr_ppid
+ struct.pgid = psinfo_array[10] # pr_pgid
+ struct.sid = psinfo_array[11] # pr_sid
+ struct.ttydev = psinfo_array[12] # pr_ttydev
+
+ # convert from 64-bit dev_t to 32-bit dev_t and then map the device
+ # number to a name
+ ttydev = struct.ttydev
+ ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF))
+ struct.s_ttydev = @devs.has_key?(ttydev) ? @devs[ttydev] : '-'
+
+ struct.addr = psinfo_array[13] # pr_addr
+ struct.size = psinfo_array[14] * 1024 # pr_size
+ struct.rssize = psinfo_array[15] * 1024 # pr_rssize
+ struct.start = Time.at(psinfo_array[16], psinfo_array[17]) # pr_start
+ # skip pr_start.__pad
+ struct.time = psinfo_array[19] # pr_time
+ # skip pr_time.tv_nsec and pr_time.__pad
+ struct.cid = psinfo_array[22] # pr_cid
+ # skip pr__pad2
+ struct.argc = psinfo_array[24] # pr_argc
+ struct.argv = psinfo_array[25] # pr_argv
+ struct.envp = psinfo_array[26] # pr_envp
+ struct.fname = psinfo_array[27] # pr_fname
+ struct.psargs = psinfo_array[28] # pr_psargs
+ # skip pr__pad
+
+ ### lwpsinfo_t info
+
+ struct.lwpid = psinfo_array[37] # pr_lwpid
+ # skip pr_addr
+ struct.wchan = psinfo_array[39] # pr_wchan
+ # skip pr_flag
+ struct.wtype = psinfo_array[41] # pr_wtype
+ struct.state = psinfo_array[42] # pr_state
+ struct.sname = psinfo_array[43] # pr_sname
+ struct.nice = psinfo_array[44] # pr_nice
+ struct.pri = psinfo_array[45] # pr_pri
+ struct.policy = psinfo_array[46] # pr_policy
+ struct.clname = psinfo_array[47] # pr_clname
+ struct.onpro = psinfo_array[48] # pr_onpro
+ struct.bindpro = psinfo_array[49] # pr_bindpro
+ struct.ptid = psinfo_array[50] # pr_ptid
+ # skip pr__pad1
+ # skip pr__pad
+
+ # Get the full command line out of /proc/<pid>/as.
+ begin
+ File.open("/proc/#{file}/as", 'rb') do |fd|
+ np = fd.sysseek(struct.argv, IO::SEEK_SET)
+
+ if np != struct.argv
+ raise Error, "argv seek to #{struct.argv}, result #{np}", caller
+ end
+
+ argv = fd.sysread(4).unpack('L')[0]
+
+ np = fd.sysseek(argv, IO::SEEK_SET)
+
+ if np != argv
+ raise Error, "*argv seek to #{argv}, result #{np}", caller
+ end
+
+ argv = fd.sysread(4 * struct.argc).unpack("L#{struct.argc}")
+
+ struct.cmd_args = []
+
+ argv.each_with_index do |address, i|
+ np = fd.sysseek(address, IO::SEEK_SET)
+
+ if np != address
+ raise Error, "argv[#{i}] seek to #{address}, result #{np}",
+ caller
+ end
+
+ data = fd.sysread(512)[/^[^\0]*/] # Null strip
+ struct.cmd_args << data
+ end
+
+ # Get the environment hash associated with the process.
+ struct.environ = {}
+
+ # First have to go to the address given by struct.envp. That will
+ # give us the address of the environment pointer array.
+
+ np = fd.sysseek(struct.envp, IO::SEEK_SET)
+
+ if np != struct.envp
+ raise Error, "envp seek to #{struct.envp}, result #{np}", caller
+ end
+
+ envloc = fd.sysread(4).unpack('L')[0]
+ n = 0
+
+ loop do
+ np = fd.sysseek(envloc, IO::SEEK_SET)
+
+ if np != envloc
+ raise Error, "envp[#{n}] seek to #{envloc}, result #{np}",
+ caller
+ end
+
+ envp = fd.sysread(4).unpack("L")[0]
+ break if envp.zero?
+ np = fd.sysseek(envp, IO::SEEK_SET)
+ data = fd.sysread(1024)[/^[^\0]*/] # Null strip
+ key, value = data.split('=')
+ struct.environ[key] = value
+ envloc += 4
+ n += 1
+ end
+ end
+ rescue Errno::EACCES, Errno::EOVERFLOW, EOFError
+ # Skip this if we don't have proper permissions, if there's
+ # no associated environment, or if there's a largefile issue.
+ rescue Errno::ENOENT
+ next # The process has terminated. Bail out!
+ end
+
+ # Information from /proc/<pid>/fd. This returns an array of
+ # numeric file descriptors used by the process.
+ struct.fd = Dir["/proc/#{file}/fd/*"].map { |f| File.basename(f).to_i }
+
+ # Use the cmd_args as the cmdline if available. Otherwise use
+ # the psargs. This struct member is provided to provide a measure
+ # of consistency with the other platform implementations.
+ if struct.cmd_args.nil? || struct.cmd_args.empty?
+ struct.cmdline = struct.psargs
+ else
+ struct.cmdline = struct.cmd_args.join(' ')
+ end
+
+ # get current working directory from /proc/<pid>/cwd
+ struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil
+
+ # get virtual address map from /proc/<pid>/map
+ begin
+ struct.map = []
+
+ File.open("/proc/#{file}/map", 'rb') do |fd|
+ loop do
+ prmap_array = fd.sysread(176).unpack(@prmap_pack_directive)
+ break if prmap_array[0].zero?
+
+ map_struct = ProcTableMapStruct.new
+
+ map_struct.size = prmap_array[0] # pr_size
+ map_struct.vaddr = prmap_array[1] # pr_vaddr
+ map_struct.mapname = prmap_array[2] # pr_mapname
+ map_struct.off = prmap_array[3] # pr_off
+ map_struct.mflags = prmap_array[4] # pr_mflags
+
+ # convert pr_mflags value to string sort of like procmap outputs
+ mflags = map_struct.mflags
+ map_struct.s_mflags = ''
+ sep = ''
+
+ PR_MFLAGS.each do |flag|
+ if (mflags & flag[0]).nonzero?
+ map_struct.s_mflags << sep << flag[1]
+ sep = '/'
+ mflags &= ~flag[0]
+ end
+ end
+
+ if mflags.nonzero?
+ map_struct.s_mflags << sep << sprintf('%08x', mflags)
+ end
+
+ map_struct.pathoff = prmap_array[5] # pr_pathoff
+ map_struct.alias = prmap_array[6] # pr_alias
+ map_struct.gp = prmap_array[7] # pr_gp
+
+ struct.map << map_struct
+ end
+
+ struct.map.each do |m|
+ next if m.pathoff.zero?
+ fd.sysseek(m.pathoff, IO::SEEK_SET)
+ buf = fd.sysread(4096)
+ buf =~ /^([^\0]*)\0([^\0]*)\0/
+ m.path = $2.empty? ? $1 : "#{$1}(#{$2})"
+ end
+ end
+
+ struct.map = nil if struct.map.empty?
+ rescue
+ struct.map = nil
+ end
+
+ # This is read-only data
+ struct.freeze
+
+ if block_given?
+ yield struct
+ else
+ array << struct
+ end
+ end
+
+ pid ? struct : array
+ end
+
+ # Returns an array of fields that each ProcTableStruct will contain. This
+ # may be useful if you want to know in advance what fields are available
+ # without having to perform at least one read of the /proc table.
+ #
+ # Example:
+ #
+ # Sys::ProcTable.fields.each do |field|
+ # puts "Field: #{field}"
+ # end
+ #
+ def self.fields
+ @fields.map{ |f| f.to_s }
+ end
+ end
+end
View
86 lib/darwin/sys/proctable.rb
@@ -0,0 +1,86 @@
+require 'ffi'
+
+module Sys
+ class ProcTable
+ extend FFI::Library
+ ffi_lib FFI::Library::LIBC
+
+ attach_function(
+ :sysctl,
+ [:pointer, :uint, :pointer, :pointer, :pointer, :size_t],
+ :int
+ )
+
+ attach_function(:strerror, [:int], :string)
+
+ private_class_method :sysctl
+
+ CTL_KERN = 1
+ KERN_PROC = 14
+ KERN_PROC_ALL = 0
+
+ # sizeof(struct kinfo_proc) == 648
+
+ # Process info appears to be stored in a rat hole of nested
+ # structs on OSX. :(
+
+ # extern_proc
+ class KpProcStruct < FFI::Struct
+ layout(
+ :p_un, UnUnion,
+ :p_vmspace, VmspaceStruct,
+
+ )
+ end
+
+ class EProcStruct < FFI::Struct
+ layout(
+ :e_paddr, ProcStruct,
+ :e_sess, SessionStruct,
+ :e_pcred, PCredStruct,
+ :e_ucred, UCredStruct,
+ :e_vm, VMSpaceStruct,
+ :e_ppid, :int,
+ :e_pgid, :int,
+ :e_jobc, :short,
+ :e_tdev, :uint, # Not sure
+ :e_tpgid, :int,
+ :e_tsess, SessionStruct,
+ :e_wmesg, [:char, 8],
+ :e_xsize, :ulong, # Not sure
+ :e_xrssize, :short,
+ :e_flag, :int32,
+ :e_login, [:char, 12],
+ :e_spare, [:int32, 4]
+ )
+ end
+
+ class KInfoStruct < FFI::Struct
+ layout(
+ :kp_proc, KpProcStruct,
+ :eproc, EProcStruct
+ )
+ end
+
+ def self.ps
+ len = FFI::MemoryPointer.new(:size_t)
+ mib = FFI::MemoryPointer.new(:int, 4)
+
+ mib.write_array_of_int([CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0])
+
+ # First pass, determine length
+ if sysctl(mib, 4, nil, len, nil, 0) < 0
+ raise Error, "sysctl function failed: " + strerror(FFI.errno)
+ end
+
+ procs = FFI::MemoryPointer.new(:pointer, len.read_long)
+ len = FFI::MemoryPointer.new(:size_t)
+
+ if sysctl(mib, 4, procs, len, nil, 0) < 0
+ raise Error, "sysctl function failed: " + strerror(FFI.errno)
+ end
+
+ # count = len.read_long / KInfoStruct.size
+ end
+ end
+end
View
10 lib/sys/top.rb
@@ -7,19 +7,21 @@ module Sys
class Top
# The version of the sys-top library
- VERSION = '1.0.3'
+ VERSION = '1.0.4'
# Returns an array of Struct::ProcTableStruct elements containing up
# to +num+ elements, sorted by +field+. The default number of elements
# is 10, while the default field is 'pctcpu'.
#
- # Exception: the default sort field is 'pid' on Linux and Windows.
+ # Exception: the default sort field is 'pid' on AIX and Windows.
#
def self.top(num=10, field='pctcpu')
field = field.to_s if field.is_a?(Symbol)
- # Sort by pid on Windows by default
- if File::ALT_SEPARATOR && field == 'pctcpu'
+ aix = RbConfig::CONFIG['host_os'] =~ /aix/i
+
+ # Sort by pid on Windows and AIX by default
+ if (File::ALT_SEPARATOR || aix) && field == 'pctcpu'
field = 'pid'
end
View
324 test/test_sys_proctable_aix.rb
@@ -0,0 +1,324 @@
+#######################################################################
+# test_sys_proctable_aix.rb
+#
+# Test suite for the AIX version of the sys-proctable library. You
+# should run these tests via the 'rake test' task.
+#######################################################################
+require 'test-unit'
+require 'sys/proctable'
+require 'test/test_sys_proctable_all'
+include Sys
+
+class TC_ProcTable_AIX < Test::Unit::TestCase
+ def self.startup
+ File.open('aix-child.rb', 'w') do |out|
+ out.puts 'trap("HUP") { exit }'
+ out.puts 'trap("TERM") { exit }'
+ out.puts 'sleep(60)'
+ end
+
+ @@myenv = ENV.to_hash
+ @@p1args = %w/aix-child.rb testing how well this works 1/
+ @@p2args = %w/aix-child.rb testing how well this works 2/
+
+ @@pid1 = fork do
+ exec('ruby', *@@p1args)
+ end
+
+ @@pid2 = fork do
+ exec('ruby', *@@p2args)
+ end
+
+ sleep(2) # wait to make sure the above execs have completed in children
+
+ @@fields = %w/
+ addr argc argv bindpro cid clname cmd_args cmdline cwd egid environ
+ envp euid fd flag flag2 fname gid lwpid map nice nlwp onpro pgid pid
+ policy ppid pri psargs ptid rssize sid size sname start state time
+ ttydev uid wchan wtype s_ttydev
+ /
+
+ @@map_fields = %w/
+ size vaddr mapname off mflags pathoff alias gp s_mflags path
+ /
+
+ @@p1info = ProcTable.ps(@@pid1)
+ @@p2info = ProcTable.ps.select { |e| e.pid == @@pid2 }
+
+ if @@p2info.size == 1
+ @@p2info = @@p2info[0]
+ else
+ $stderr.puts "expected a single process, have #{@@p2info.size}"
+ exit 1
+ end
+ end
+
+ def test_expected_fields
+ assert_respond_to(ProcTable, :fields)
+ assert_kind_of(Array, ProcTable.fields)
+ assert_equal(@@fields.sort, ProcTable.fields.sort)
+ end
+
+ def test_flag
+ assert_respond_to(@@p1info, :flag)
+ assert_kind_of(Fixnum, @@p1info.flag)
+ end
+
+ def test_flag2
+ assert_respond_to(@@p1info, :flag2)
+ assert_kind_of(Fixnum, @@p1info.flag2)
+ end
+
+ def test_nlwp
+ assert_respond_to(@@p1info, :nlwp)
+ assert_kind_of(Fixnum, @@p1info.nlwp)
+ end
+
+ def test_uid
+ assert_respond_to(@@p1info, :uid)
+ assert_kind_of(Integer, @@p1info.uid)
+ assert_equal(Process.uid, @@p1info.uid)
+ end
+
+ def test_euid
+ assert_respond_to(@@p1info, :euid)
+ assert_kind_of(Integer, @@p1info.euid)
+ assert_equal(Process.euid, @@p1info.euid)
+ end
+
+ def test_gid
+ assert_respond_to(@@p1info, :gid)
+ assert_kind_of(Integer, @@p1info.gid)
+ assert_equal(Process.gid, @@p1info.gid)
+ end
+
+ def test_egid
+ assert_respond_to(@@p1info, :egid)
+ assert_kind_of(Integer, @@p1info.egid)
+ assert_equal(Process.egid, @@p1info.egid)
+ end
+
+ def test_pid
+ assert_respond_to(@@p1info, :pid)
+ assert_kind_of(Integer, @@p1info.pid)
+ assert_equal(@@pid1, @@p1info.pid)
+ end
+
+ def test_ppid
+ assert_respond_to(@@p1info, :ppid)
+ assert_kind_of(Integer, @@p1info.ppid)
+ assert_equal(Process.pid, @@p1info.ppid)
+ end
+
+ def test_pgid
+ assert_respond_to(@@p1info, :pgid)
+ assert_kind_of(Integer, @@p1info.pgid)
+ assert_equal(Process.getpgrp, @@p1info.pgid)
+ end
+
+ def test_sid
+ assert_respond_to(@@p1info, :sid)
+ assert_kind_of(Integer, @@p1info.sid)
+ end
+
+ def test_ttydev
+ assert_respond_to(@@p1info, :ttydev)
+ assert_kind_of(Integer, @@p1info.ttydev)
+ end
+
+ def test_addr
+ assert_respond_to(@@p1info, :addr)
+ assert_kind_of(Integer, @@p1info.addr)
+ end
+
+ def test_size
+ assert_respond_to(@@p1info, :size)
+ assert_kind_of(Integer, @@p1info.size)
+ end
+
+ def test_rssize
+ assert_respond_to(@@p1info, :rssize)
+ assert_kind_of(Integer, @@p1info.rssize)
+ end
+
+ def test_start
+ assert_respond_to(@@p1info, :start)
+ assert_kind_of(Time, @@p1info.start)
+ end
+
+ def test_time
+ assert_respond_to(@@p1info, :time)
+ assert_kind_of(Integer, @@p1info.time)
+ end
+
+ def test_cid
+ assert_respond_to(@@p1info, :cid)
+ assert_kind_of(Fixnum, @@p1info.cid)
+ end
+
+ def test_argc
+ assert_respond_to(@@p1info, :argc)
+ assert_kind_of(Fixnum, @@p1info.argc)
+ assert_equal(@@p1args.size + 1, @@p1info.argc)
+ end
+
+ def test_argv
+ assert_respond_to(@@p1info, :argv)
+ assert_kind_of(Integer, @@p1info.argv)
+ end
+
+ def test_envp
+ assert_respond_to(@@p1info, :envp)
+ assert_kind_of(Integer, @@p1info.envp)
+ end
+
+ def test_fname
+ assert_respond_to(@@p1info, :fname)
+ assert_kind_of(String, @@p1info.fname)
+ end
+
+ def test_psargs
+ assert_respond_to(@@p1info, :psargs)
+ assert_kind_of(String, @@p1info.psargs)
+ end
+
+ def test_lwpid
+ assert_respond_to(@@p1info, :lwpid)
+ assert_kind_of(Integer, @@p1info.lwpid)
+ end
+
+ def test_wchan
+ assert_respond_to(@@p1info, :wchan)
+ assert_kind_of(Integer, @@p1info.wchan)
+ end
+
+ def test_wtype
+ assert_respond_to(@@p1info, :wtype)
+ assert_kind_of(Fixnum, @@p1info.wtype)
+ end
+
+ def test_state
+ assert_respond_to(@@p1info, :state)
+ assert_kind_of(Fixnum, @@p1info.state)
+ end
+
+ def test_sname
+ assert_respond_to(@@p1info, :sname)
+ assert_kind_of(String, @@p1info.sname)
+ end
+
+ def test_nice
+ assert_respond_to(@@p1info, :nice)
+ assert_kind_of(Fixnum, @@p1info.nice)
+ end
+
+ def test_pri
+ assert_respond_to(@@p1info, :pri)
+ assert_kind_of(Fixnum, @@p1info.pri)
+ end
+
+ def test_policy
+ assert_respond_to(@@p1info, :policy)
+ assert_kind_of(Fixnum, @@p1info.policy)
+ end
+
+ def test_clname
+ assert_respond_to(@@p1info, :clname)
+ assert_kind_of(String, @@p1info.clname)
+ end
+
+ def test_onpro
+ assert_respond_to(@@p1info, :onpro)
+ assert_kind_of(Fixnum, @@p1info.onpro)
+ end
+
+ def test_bindpro
+ assert_respond_to(@@p1info, :bindpro)
+ assert_kind_of(Fixnum, @@p1info.bindpro)
+ end
+
+ def test_ptid
+ assert_respond_to(@@p1info, :ptid)
+ assert_kind_of(Fixnum, @@p1info.ptid)
+ end
+
+ def test_comm
+ assert_respond_to(@@p1info, :comm)
+ assert_kind_of(String, @@p1info.comm)
+ end
+
+ def test_fd
+ assert_respond_to(@@p1info, :fd)
+ assert_kind_of(Array, @@p1info.fd)
+ end
+
+ def test_cmd_args
+ assert_respond_to(@@p1info, :cmd_args)
+ assert_kind_of(Array, @@p1info.cmd_args)
+ assert_equal([ 'ruby', @@p1args ].flatten, @@p1info.cmd_args)
+
+ assert_respond_to(@@p2info, :cmd_args)
+ assert_kind_of(Array, @@p2info.cmd_args)
+ assert_equal([ 'ruby', @@p2args ].flatten, @@p2info.cmd_args)
+ end
+
+ def test_environ
+ assert_respond_to(@@p1info, :environ)
+ assert_kind_of(Hash, @@p1info.environ)
+ assert_equal(@@myenv, @@p1info.environ)
+
+ assert_respond_to(@@p2info, :environ)
+ assert_kind_of(Hash, @@p2info.environ)
+ assert_equal(@@myenv, @@p2info.environ)
+ end
+
+ def test_cmdline
+ assert_respond_to(@@p1info, :cmdline)
+ assert_kind_of(String, @@p1info.cmdline)
+ end
+
+ def test_cwd
+ assert_respond_to(@@p1info, :cwd)
+ assert_kind_of([String, NilClass], @@p1info.cwd)
+ end
+
+ def test_map
+ assert_respond_to(@@p1info, :map)
+ assert_kind_of([Array, NilClass], @@p1info.map)
+ end
+
+ def test_map_struct
+ assert_not_nil(@@p1info.map)
+ assert_equal(@@map_fields.sort, @@p1info.map[0].members.sort)
+ end
+
+ def test_map_print
+ assert_not_nil(@@p1info.map)
+
+ @@p1info.map.each do |m|
+ assert_kind_of(Struct::ProcTableMapStruct, m)
+ assert_kind_of(Integer, m.size)
+ assert_kind_of(Integer, m.vaddr)
+ assert_kind_of(String, m.mapname)
+ assert_kind_of(Integer, m.size)
+ assert_kind_of(Integer, m.off)
+ assert_kind_of(Integer, m.mflags)
+ assert_kind_of(String, m.s_mflags)
+ assert_kind_of(Integer, m.pathoff)
+ assert_kind_of(Integer, m.alias)
+ assert_kind_of(Integer, m.gp)
+ assert_kind_of(String, m.path)
+ end
+ end
+
+ def self.shutdown
+ Process.kill('TERM', @@pid1)
+ Process.kill('TERM', @@pid2)
+ File.unlink('aix-child.rb') rescue nil
+ @@myenv = nil
+ @@pid1 = nil
+ @@pid2 = nil
+ @@p1info = nil
+ @@p2info = nil
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.