Unraveling binary data out of the proc filesystem on Solaris

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//psinfo.

However, where I’m having trouble is getting the command line
arguments out of /proc//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[i]) < 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//as ?

Thanks,

Dan

Hi,

2008/8/26 Daniel B. [email protected]:

arguments out of /proc//as. Basically, I need to know how to
next if pid && file.to_i != pid
asinfo = IO.read(“/proc/#{file}/as”, size, addr)
ProcTable.ps(2764) # or whatever pid you prefer
}
if(pread(fd, arg, arg_len, arg_vec[i]) < 0)
}
free(arg);
free(arg_vec);
}

close(fd);

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

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

/proc//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//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 H.

On Aug 25, 7:59 pm, “Heesob P.” [email protected] wrote:

For the basic information, this is fairly straightforward, as I read
class ProcTable
argc = psinfo[188,4].unpack(“L”)[0] # pr_argc
end
arg_count = p.pr_argc; /* from the psinfo struct */
arg_len = 16;
i–;
Any ideas how to get the string(s) I need out of /proc//as ?

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

/proc//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//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.

or changed through read(2) or write(2) (or by using pread(2) or
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

On Aug 26, 8:16 am, Daniel B. [email protected] wrote:

Ruby 1.8.6
Solaris10

I’m trying to get process information on aSolarisusing pure Ruby.
For the basic information, this is fairly straightforward, as I read
out of /proc//psinfo.

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

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//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. :confused:

On Aug 28, 2008, at 4:04 PM, Daniel B. wrote:

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.

ruby-dl ??

a @ http://codeforpeople.com/

On Aug 28, 5:48 pm, “ara.t.howard” [email protected] wrote:

On Aug 28, 2008, at 4:04 PM, Daniel B. wrote:

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.

ruby-dl ??

It’s a dependency either way. I prefer the one I know…

Regards,

Dan

2008/8/26 Daniel B. [email protected]:

On Aug 25, 7:59 pm, “Heesob P.” [email protected] wrote:

Hi,

2008/8/26 Daniel B. [email protected]:

>

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.

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 H.

2008/8/29 Daniel B. [email protected]:

>
Seeks to a given offset in the stream according to the value of
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 H.

On Aug 28, 7:53 pm, Daniel B. [email protected] wrote:


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

On Aug 28, 7:37 pm, “Heesob P.” [email protected] wrote:

Ruby has no lseek that I’m aware of. I tried an open + seek + read

    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.

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

On Aug 28, 2008, at 6:42 PM, Daniel B. wrote:

It’s a dependency either way. I prefer the one I know…

?? ruby dl is built in. if you access pread via that there is no
dependancy which is not pure-ruby. just food for thought.

a @ http://codeforpeople.com/

On Aug 29, 2008, at 4:33 AM, Daniel B. wrote:

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/

On Aug 28, 10:40 pm, “ara.t.howard” [email protected] wrote:

On Aug 28, 2008, at 6:42 PM, Daniel B. wrote:

It’s a dependency either way. I prefer the one I know…

?? ruby dl is built in. if you access pread via that there is no
dependancy which is not pure-ruby. just 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