FFI, sysctl, pointer question

Hi,

I'm trying to get better at FFI, but I'm getting stumped on how to create and pass pointers to functions.

In the example below, in the Sys::Uptime.seconds method, I'm trying to figure out how to create and pass a pointer for the mib (a 2-element integer array) and the timeval size (i.e. where I have a '?' instead of actual code).

What should the actual code be?

module Sys
    class Uptime
       extend FFI::Library

       attach_function :time, [:pointer], :ulong
       attach_function :sysctl,
          [:pointer, :uint, :pointer, :pointer, :pointer, :uint], :int

       CTL_KERN = 1 # Kernel
       KERN_BOOTTIME = 21 # Time kernel was booted

       class Timeval < FFI::Struct
          layout(
             :tv_sec, :long,
             :tv_usec, :long
          )
       end

       # How do I create the mib? How do I pass the address of tv.size?
       def self.seconds
          tv = Timeval.new
          mib = [CTL_KERN, KERN_BOOTTIME]

          # What's the proper way to call this?
          if sysctl(?, 2, tv, ?, nil, 0) != 0
             raise SystemCallError, 'sysctl()'
          end

          time(nil) - tv[:tv_sec]
       end
    end
end

Regards,

Dan

At first sight you need MemoryPointer, but I could not really get it
working, but probably I screwed the sysctl call, I did not find a
reference of it for my system. Well for what it is worth I'll share
what I did.
*N.B.* This fails with
ffi1.rb:35:in `seconds': unknown error - sysctl() (SystemCallError)
  from ffi1.rb:43:in `<main>'

    def self.seconds
        tv = Timeval.new
        mib = [CTL_KERN, KERN_BOOTTIME]

        # What's the proper way to call this?
        tvp = FFI::MemoryPointer::new :pointer
        tvp.put_pointer 0, tv
        mip = FFI::MemoryPointer::new :int, 2
        mip.put_array_of_int 0, mib
        if sysctl(mip, 2, tv, tvp, nil, 0) != 0
           raise SystemCallError, 'sysctl()'
        end

        time(nil) - tv[:tv_sec]
end

Maybe somebody can spot my error(s) :wink:

Cheers
Robert

···

On Fri, Aug 7, 2009 at 10:22 AM, Daniel Berger<djberg96@gmail.com> wrote:

--
module Kernel
  alias_method :λ, :lambda
end

Daniel Berger wrote:

Hi,

I'm trying to get better at FFI, but I'm getting stumped on how to
create and pass pointers to functions.

In the example below, in the Sys::Uptime.seconds method, I'm trying to
figure out how to create and pass a pointer for the mib (a 2-element
integer array) and the timeval size (i.e. where I have a '?' instead of
actual code).

What should the actual code be?

You could explicity instantiate two MemoryPointer objects initialized
with
the proper values:

  mib_ptr = FFI::MemoryPointer.new(:int, 2).write_array_of_int(mib)
  tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)

Then you could pass those values to sysctl function:

  sysctl(mib_ptr, 2, tv, tv_size_ptr, nil, 0)

To ask for more (and better) help feel free to post your questions to
the Ruby-FFI ml at users@ruby-ffi.kenai.com

···

--
Posted via http://www.ruby-forum.com/\.

Hello,

size_t in ANSI C is required to be unsigned and is usually defined as
unsigned long. Therefore the above code is always wrong in that it
stores a signed integer, and it is likely wrong on some 64 bit systems
where an int is 32 bits wide but a long is 64 bits wide.

cu,
Thomas

···

2009/8/7 Andrea Fazzi <andrea.fazzi@alcacoop.it>:

[...]
tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)
[...]

--
When C++ is your hammer, every problem looks like your thumb.

Andrea Fazzi wrote:

Daniel Berger wrote:

Hi,

I'm trying to get better at FFI, but I'm getting stumped on how to
create and pass pointers to functions.

In the example below, in the Sys::Uptime.seconds method, I'm trying to
figure out how to create and pass a pointer for the mib (a 2-element
integer array) and the timeval size (i.e. where I have a '?' instead of
actual code).

What should the actual code be?

You could explicity instantiate two MemoryPointer objects initialized with
the proper values:

  mib_ptr = FFI::MemoryPointer.new(:int, 2).write_array_of_int(mib)
  tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)

Then you could pass those values to sysctl function:

  sysctl(mib_ptr, 2, tv, tv_size_ptr, nil, 0)

Thank you, that worked.

To ask for more (and better) help feel free to post your questions to the Ruby-FFI ml at users@ruby-ffi.kenai.com

If we want FFI to gain traction among Rubyists (we do, don't we?), it's better to ask & answer here IMO.

Regards,

Dan

Thomas Chust wrote:

�[...]
�tv_size_ptr = FFI::MemoryPointer.new(:int).write_int(tv.size)
[...]

Hello,

size_t in ANSI C is required to be unsigned and is usually defined as
unsigned long. Therefore the above code is always wrong in that it
stores a signed integer, and it is likely wrong on some 64 bit systems
where an int is 32 bits wide but a long is 64 bits wide.

cu,
Thomas

Hi Thomas,

I didn't check for the sysctl prototype when replied to OP so I was not
aware about the type of the fourth argument (a pointer to a size_t
value). Thank you for pointing it out. That said, the MemoryPointer
object should be instantiated and filled in this way:

tv_size_ptr = FFI::MemoryPointer.new(:size_t).write_size_t(tv.size)

Doing this way, Ruby-FFI should use the right size for size_t.
Unfortunately, AFAIK, while :size_t type exists its accessors
(get_size_t, read_size_t, put_size_t, write_size_t) are not yet
implemented on Ruby-FFI. I'll fire a JIRA ticket for this.

Please also note that my previous code is not *always* wrong. Well, it
may be conceptually wrong but not in practice for the considered case.
In fact, size of the Timeval struct is 8 bytes on a ILP32 system and 16
bytes on a LP64 one. Thus, writing/reading a pointer to a signed 8 (or
16) is the same as writing/reading a pointer to an unsigned 8 (or 16).

signed = FFI::MemoryPointer.new(:int).put_int(0, 8)

=> #<MemoryPointer address=0xa301cd0 size=4>

signed.get_int(0)

=> 8

signed.get_uint(0)

=> 8

unsigned = FFI::MemoryPointer.new(:uint).put_uint(0, 8)

=> #<MemoryPointer address=0xa2ae518 size=4>

unsigned.get_int(0)

=> 8

unsigned.get_uint(0)

=> 8

Andrea

···

2009/8/7 Andrea Fazzi <andrea.fazzi@alcacoop.it>:

--
Posted via http://www.ruby-forum.com/\.