FFI, sysctl, pointer question

D

Daniel Berger

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
 
R

Robert Dober

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 =3D Timeval.new
mib =3D [CTL_KERN, KERN_BOOTTIME]

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

time(nil) - tv[:tv_sec]
end

Maybe somebody can spot my error(s) ;)

Cheers
Robert

--=20
module Kernel
alias_method :=EB, :lambda
end
 
A

Andrea Fazzi

Daniel said:
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 (e-mail address removed)
 
T

Thomas Chust

2009/8/7 Andrea Fazzi said:
=A0[...]
=A0tv_size_ptr =3D 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


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

Andrea Fazzi

Thomas said:
2009/8/7 Andrea Fazzi said:
�[...]
�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).
=> 8

Andrea
 
D

Daniel Berger

Andrea said:
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 (e-mail address removed)

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

Regards,

Dan
 

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,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top