Unraveling binary data out of the proc filesystem on Solaris

D

Daniel Berger

Hi all,

Ruby 1.8.6
Solaris 10

I'm trying to get process information on a Solaris using pure Ruby.
For the basic information, this is fairly straightforward, as I read
out of /proc/<pid>/psinfo.

However, where I'm having trouble is getting the command line
arguments out of /proc/<pid>/as. Basically, I need to know how to
unravel the data I'm reading into a human readable string.

Here's some sample code:

# sunos.rb
module Sys
class ProcTable
def self.ps(pid = nil)
Dir.foreach("/proc") do |file|
next if file =~ /\D/
next if pid && file.to_i != pid

psinfo = IO.read("/proc/#{file}/psinfo")

pid = psinfo[8,4].unpack("L")[0]
puts "PID: #{pid}"

argc = psinfo[188,4].unpack("L")[0] # pr_argc
addr = psinfo[192,4].unpack("L")[0] # pr_argv

size = argc * 4 # 4 is the sizeof(caddr32_t)
asinfo = IO.read("/proc/#{file}/as", size, addr)

# How do I unravel asinfo?
p asinfo
end
end
end
end

if $0 == __FILE__
include Sys
ProcTable.ps(2764) # or whatever pid you prefer
end

The C code I'm trying to simulate, which I pulled from a post by Roger
Faulkner on comp.unix.questions, looks something like this:

addr_args = p.pr_argv; /* from the psinfo struct */
arg_count = p.pr_argc; /* from the psinfo struct */

if((fd = open(as_file, O_RDONLY)) < 0){
/* Do nothing - you can only get info on processes you rights to */
}
else
{
arg_vec = malloc(arg_count * sizeof(uintptr_t));
(void)pread(fd, arg_vec, arg_count * sizeof (uintptr_t),
addr_args);

arg_len = 16;
arg = malloc(arg_len+1);

for(i = 0; i < arg_count; i++) {
if(pread(fd, arg, arg_len, arg_vec) < 0)
continue;

arg[arg_len] = '\0';
if(strlen(arg) == arg_len){
arg_len *= 2;
arg = realloc(arg, arg_len + 1);
i--;
continue;
}
rb_ary_push(v_cmd_array, rb_str_new2(arg));
}
free(arg);
free(arg_vec);
}

close(fd);

Any ideas how to get the string(s) I need out of /proc/<pid>/as ?

Thanks,

Dan
 
H

Heesob Park

Hi,

2008/8/26 Daniel Berger said:
Hi all,

Ruby 1.8.6
Solaris 10

I'm trying to get process information on a Solaris using pure Ruby.
For the basic information, this is fairly straightforward, as I read
out of /proc/<pid>/psinfo.

However, where I'm having trouble is getting the command line
arguments out of /proc/<pid>/as. Basically, I need to know how to
unravel the data I'm reading into a human readable string.

Here's some sample code:

# sunos.rb
module Sys
class ProcTable
def self.ps(pid = nil)
Dir.foreach("/proc") do |file|
next if file =~ /\D/
next if pid && file.to_i != pid

psinfo = IO.read("/proc/#{file}/psinfo")

pid = psinfo[8,4].unpack("L")[0]
puts "PID: #{pid}"

argc = psinfo[188,4].unpack("L")[0] # pr_argc
addr = psinfo[192,4].unpack("L")[0] # pr_argv

size = argc * 4 # 4 is the sizeof(caddr32_t)
asinfo = IO.read("/proc/#{file}/as", size, addr)

# How do I unravel asinfo?
p asinfo
end
end
end
end

if $0 == __FILE__
include Sys
ProcTable.ps(2764) # or whatever pid you prefer
end

The C code I'm trying to simulate, which I pulled from a post by Roger
Faulkner on comp.unix.questions, looks something like this:

addr_args = p.pr_argv; /* from the psinfo struct */
arg_count = p.pr_argc; /* from the psinfo struct */

if((fd = open(as_file, O_RDONLY)) < 0){
/* Do nothing - you can only get info on processes you rights to */
}
else
{
arg_vec = malloc(arg_count * sizeof(uintptr_t));
(void)pread(fd, arg_vec, arg_count * sizeof (uintptr_t),
addr_args);

arg_len = 16;
arg = malloc(arg_len+1);

for(i = 0; i < arg_count; i++) {
if(pread(fd, arg, arg_len, arg_vec) < 0)
continue;

arg[arg_len] = '\0';
if(strlen(arg) == arg_len){
arg_len *= 2;
arg = realloc(arg, arg_len + 1);
i--;
continue;
}
rb_ary_push(v_cmd_array, rb_str_new2(arg));
}
free(arg);
free(arg_vec);
}

close(fd);

Any ideas how to get the string(s) I need out of /proc/<pid>/as ?

Roger Faulkner mentioned in the com.unix.solraris like this:

/proc/<pid>/as is not your ordinary sparse file.
Attempts to read from non-existent portions of it yield a zero read().

The accessible portions of the as file are described by the
/proc/<pid>/map file. Reading the map file gives you an array
of structures describing the legitimate addresses in the process.

Read (and re-read) the proc(4) man page documentation

Refer to http://unix.derkeiler.com/Newsgroups/comp.unix.solaris/2003-05/3189.html

According to the man page proc(4):

as
Contains the address-space image of the process; it can be opened for
both reading and writing. lseek(2) is used to position the file at the
virtual address of interest and then the address space can be examined
or changed through read(2) or write(2) (or by using pread(2) or
pwrite(2) for the combined operation).

According to the man page pread(2):

The pread() function performs the same action as read(), except that
it reads from a given position in the file without changing the file
pointer. The first three arguments to pread() are the same as read()
with the addition of a fourth argument offset for the desired position
inside the file. pread() will read up to the maximum offset value that
can be represented in an off_t for regular files. An attempt to
perform a pread() on a file that is incapable of seeking results in an
error.

I guess you should use lseek and read combination.

HTH,

Park Heesob
 
D

Daniel Berger

Hi,

2008/8/26 Daniel Berger said:
Ruby 1.8.6
Solaris 10
I'm trying to get process information on a Solaris using pure Ruby.
For the basic information, this is fairly straightforward, as I read
out of /proc/<pid>/psinfo.
However, where I'm having trouble is getting the command line
arguments out of /proc/<pid>/as. Basically, I need to know how to
unravel the data I'm reading into a human readable string.
Here's some sample code:
# sunos.rb
module Sys
class ProcTable
def self.ps(pid = nil)
Dir.foreach("/proc") do |file|
next if file =~ /\D/
next if pid && file.to_i != pid
psinfo = IO.read("/proc/#{file}/psinfo")
pid = psinfo[8,4].unpack("L")[0]
puts "PID: #{pid}"
argc = psinfo[188,4].unpack("L")[0] # pr_argc
addr = psinfo[192,4].unpack("L")[0] # pr_argv
size = argc * 4 # 4 is the sizeof(caddr32_t)
asinfo = IO.read("/proc/#{file}/as", size, addr)
# How do I unravel asinfo?
p asinfo
end
end
end
end
if $0 == __FILE__
include Sys
ProcTable.ps(2764) # or whatever pid you prefer
end
The C code I'm trying to simulate, which I pulled from a post by Roger
Faulkner on comp.unix.questions, looks something like this:
addr_args = p.pr_argv; /* from the psinfo struct */
arg_count = p.pr_argc; /* from the psinfo struct */
if((fd = open(as_file, O_RDONLY)) < 0){
/* Do nothing - you can only get info on processes you rights to */
}
else
{
arg_vec = malloc(arg_count * sizeof(uintptr_t));
(void)pread(fd, arg_vec, arg_count * sizeof (uintptr_t),
addr_args);
arg_len = 16;
arg = malloc(arg_len+1);
for(i = 0; i < arg_count; i++) {
if(pread(fd, arg, arg_len, arg_vec) < 0)
continue;

arg[arg_len] = '\0';
if(strlen(arg) == arg_len){
arg_len *= 2;
arg = realloc(arg, arg_len + 1);
i--;
continue;
}
rb_ary_push(v_cmd_array, rb_str_new2(arg));
}
free(arg);
free(arg_vec);
}
close(fd);

Any ideas how to get the string(s) I need out of /proc/<pid>/as ?

Roger Faulkner mentioned in the com.unix.solraris like this:

/proc/<pid>/as is not your ordinary sparse file.
Attempts to read from non-existent portions of it yield a zero read().

The accessible portions of the as file are described by the
/proc/<pid>/map file. Reading the map file gives you an array
of structures describing the legitimate addresses in the process.


That shouldn't be necessary. I don't do that in the C code. I wouldn't
think I would have to do that in Ruby.
Read (and re-read) the proc(4) man page documentation

Refer tohttp://unix.derkeiler.com/Newsgroups/comp.unix.solaris/2003-05/3189.html

According to the man page proc(4):

as
Contains the address-space image of the process; it can be opened for
both reading and writing. lseek(2) is used to position the file at the
virtual address of interest and then the address space can be examined
or changed through read(2) or write(2) (or by using pread(2) or
pwrite(2) for the combined operation).

According to the man page pread(2):

The pread() function performs the same action as read(), except that
it reads from a given position in the file without changing the file
pointer. The first three arguments to pread() are the same as read()
with the addition of a fourth argument offset for the desired position
inside the file. pread() will read up to the maximum offset value that
can be represented in an off_t for regular files. An attempt to
perform a pread() on a file that is incapable of seeking results in an
error.

I guess you should use lseek and read combination.

Ruby has no lseek that I'm aware of. I tried an open + seek + read
approach, but I get the same result.

I guess I'll have to wrap pread and pwrite unless there's a way to
read from a specific point in a file without moving the file pointer
in Ruby that I don't know of.

Maybe I'll tinker with mmap.

Thanks,

Dan
 
D

Daniel Berger

<snip>

The bad news is that it requires pread() after all as far as I can
tell. The good news is that I've added pread and pwrite support to the
io-extra library, and it's available in io-extra-1.1.0 and later.

Here's a snippet:

require 'io/extra'

# Mozilla on Solaris. Use whatever pid you wish.
#
# ps -ef shows the args to be "/bin/ksh", "-p", and "/usr/sfw/bin/
mozilla"
#
pid = 2253

# First, get the psinfo struct data and unravel some information we
need
psinfo = IO.read("/proc/#{pid}/psinfo")
pr_argc = psinfo[188, 4].unpack("L")[0]
pr_argv = psinfo[192, 4].unpack("L")[0]

# Second, get our initial address from /proc/<pid>/as
fd = File.open("/proc/#{pid}/as")
addr = IO.pread(fd.fileno, pr_argc * 4, pr_argv).unpack("L")[0]

# Third, get each command argument, incrementing the address as
needed. I've
# hard coded the read limit at 128. Adjust as you see fit.
0.upto(pr_argc - 1){ |i|
data = IO.pread(fd.fileno, 128, addr)
addr += data.length + 1 # Add 1 for the space
p data
}

# Finally, close our file descriptor
fd.close

On my system, that prints "/bin/ksh", "-p" and "/usr/sfw/bin/mozilla"
as expected.

Regards,

Dan

PS - Doing this also helped me realize that there's a bug in the C
implementation, too. :/
 
H

Heesob Park

2008/8/26 Daniel Berger said:
Ruby has no lseek that I'm aware of. I tried an open + seek + read
approach, but I get the same result.
Are you unaware IO#sysseek ? sysseek is actualy lseek.

------------------------------------------------------------- IO#sysseek
ios.sysseek(offset, whence=SEEK_SET) => integer
------------------------------------------------------------------------
Seeks to a given _offset_ in the stream according to the value of
_whence_ (see +IO#seek+ for values of _whence_). Returns the new
offset into the file.

f = File.new("testfile")
f.sysseek(-13, IO::SEEK_END) #=> 53
f.sysread(10) #=> "And so on."

I guess open + sysseek +sysread will work for you.


Regards,

Park Heesob
 
D

Daniel Berger

Are you unaware IO#sysseek ? sysseek is actualy lseek.

------------------------------------------------------------- IO#sysseek
=A0 =A0 =A0ios.sysseek(offset, whence=3DSEEK_SET) =A0 =3D> integer
------------------------------------------------------------------------
=A0 =A0 =A0Seeks to a given _offset_ in the stream according to the value= of
=A0 =A0 =A0_whence_ (see +IO#seek+ for values of _whence_). Returns the n= ew
=A0 =A0 =A0offset into the file.

=A0 =A0 =A0 =A0 f =3D File.new("testfile")
=A0 =A0 =A0 =A0 f.sysseek(-13, IO::SEEK_END) =A0 #=3D> 53
=A0 =A0 =A0 =A0 f.sysread(10) =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0#=3D> "A= nd so on."

I guess open + sysseek +sysread will work for you.

I tried sysseek on RHEL, but couldn't make it work. It's possible I
did something wrong, but I'm not sure what.

I also see nothing in the lseek or sysseek documentation that says it
doesn't move the file pointer.

Regards,

Dan
 
H

Heesob Park

2008/8/29 Daniel Berger said:
I tried sysseek on RHEL, but couldn't make it work. It's possible I
did something wrong, but I'm not sure what.

I also see nothing in the lseek or sysseek documentation that says it
doesn't move the file pointer.
Did you check replacing pread with lseek and read combination in C
code implementation not working? If then pread is the only way.


Regards,

Park Heesob
 
D

Daniel Berger

"And so on."


I tried sysseek on RHEL, but couldn't make it work. It's possible I
did something wrong, but I'm not sure what.

Er, Solaris 10, not RHEL. Whoops.

Dan
 
D

Daniel Berger

?? ruby dl is built in. =A0if you access pread via that there is no =A0
dependancy which is not pure-ruby. =A0just food for thought.

Oh, that's right. I was thinking ruby-inline. Anyway, I thought ruby-
dl had 64 bit issues. I've no idea what the status of dl2 is, though.

Thanks,

Dan
 
A

ara.t.howard

Oh, that's right. I was thinking ruby-inline. Anyway, I thought ruby-
dl had 64 bit issues. I've no idea what the status of dl2 is, though.

hrm - well it works on osx and that's 64 bit - but i honestly haven't
used it for quite some time. just seemed worth considering when a
single function is needed - there is an example for libc stuff in the
dist.

cheers.

a @ http://codeforpeople.com/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,579
Members
45,053
Latest member
BrodieSola

Latest Threads

Top