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/<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[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/<pid>/as ?

Thanks,

Dan

Hi,

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[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/<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

···

2008/8/26 Daniel Berger <djberg96@gmail.com>:

Hi,

> 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[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/<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

···

On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:

2008/8/26 Daniel Berger <djber...@gmail.com>:

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

···

On Aug 26, 8:16 am, Daniel Berger <djber...@gmail.com> wrote:

On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:

> Hi,

> 2008/8/26 DanielBerger<djber...@gmail.com>:

> > Hi all,

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

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

Hi,

<snip>>

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

···

2008/8/26 Daniel Berger <djberg96@gmail.com>:

On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:

2008/8/26 Daniel Berger <djber...@gmail.com>:

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

ruby-dl ??

a @ http://codeforpeople.com/

···

On Aug 28, 2008, at 4:04 PM, Daniel Berger 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.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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, 7:37 pm, "Heesob Park" <pha...@gmail.com> wrote:

2008/8/26 Daniel Berger <djber...@gmail.com>:

> On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:
>> Hi,

>> 2008/8/26 Daniel Berger <djber...@gmail.com>:

<snip>>
>> 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\(&quot;testfile&quot;\)
    f\.sysseek\(\-13, IO::SEEK\_END\)   \#=&gt; 53
    f\.sysread\(10\)                  \#=&gt; &quot;And so on\.&quot;

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

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

Regards,

Dan

···

On Aug 28, 5:48 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

On Aug 28, 2008, at 4:04 PM, Daniel Berger 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 ??

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

···

2008/8/29 Daniel Berger <djberg96@gmail.com>:

On Aug 28, 7:37 pm, "Heesob Park" <pha...@gmail.com> wrote:

2008/8/26 Daniel Berger <djber...@gmail.com>:

> On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:
>> Hi,

>> 2008/8/26 Daniel Berger <djber...@gmail.com>:

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

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.

Er, Solaris 10, not RHEL. Whoops.

Dan

···

On Aug 28, 7:53 pm, Daniel Berger <djber...@gmail.com> wrote:

On Aug 28, 7:37 pm, "Heesob Park" <pha...@gmail.com> wrote:

> 2008/8/26 Daniel Berger <djber...@gmail.com>:

> > On Aug 25, 7:59 pm, "Heesob Park" <pha...@gmail.com> wrote:
> >> Hi,

> >> 2008/8/26 Daniel Berger <djber...@gmail.com>:

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

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

?? 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 28, 2008, at 6:42 PM, Daniel Berger wrote:

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

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama

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

···

On Aug 28, 10:40 pm, "ara.t.howard" <ara.t.how...@gmail.com> wrote:

On Aug 28, 2008, at 6:42 PM, Daniel Berger 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.

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 29, 2008, at 4:33 AM, Daniel Berger 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.

--
we can deny everything, except that we have the possibility of being better. simply reflect on that.
h.h. the 14th dalai lama