FFI, getutxent question

Hi

According to the man page, the prototypes for getutxent(), setutxent()
and endutxent() are as follows:

void endutxent(void);
struct utmpx * getutxent(void);
void setutxent(void);

So, I declared them like so:

require ‘ffi’

class Info
extend FFI::Library

attach_function :setutxent, [], :void
attach_function :getutxent, [], :pointer
attach_function :endutxent, [], :void

def self.getstuff
   begin
      setutxent()
      p getutxent() # NULL Pointer ???
   ensure
      endutxent()
   end
end

end

Info.getstuff # Null Pointer object

However, the call to getutxent() returns a NULL pointer. I’m guessing I
have to tell it what kind of pointer to return, but I wasn’t sure how to
do that. How do I do that?

Regards,

Dan

Daniel B. wrote:

Info.getstuff # Null Pointer object

Your script runs smoothly on my system (Ubuntu Linux) returning a proper
NativePointer object. It seems that the issue is related to your
environment rather then FFI.

However, the call to getutxent() returns a NULL pointer. I’m guessing I
have to tell it what kind of pointer to return, but I wasn’t sure how to
do that. How do I do that?

You don’t. Once you get a proper pointer object you’ll use it to
instantiate the Utmpx class:

Utmpx.new(getutxent)

The Utmpx class will be a subclass of FFI::Struct that mirrors the
layout of the C counterpart. Moreover, as I can see from libc reference
manual[1], utmpx has nested structs within. The good news is that
Ruby-FFI has support for nested structs. The bad one is that you have to
define all the nested structs by hand:

class Pid < FFI::Struct
layout …
end

class Utmpx < FFI::Struct
layout :ut_type, :short,
:ut_pid, Pid

end

Regards,
Andrea

Daniel B. wrote:

I tried this but I’m still having trouble. Part of the problem may be
that I’m not sure how to declare char[] struct members. The docs
indicate there’s a :char_array type, but FFI doesn’t seem to like it.

Yeah, the docs on the Kenai wiki are pretty out of date at the moment.
We’ll fix them when we’ll finish the migration on github. Basically, you
can declare array types in this way:

[type, num]

where type can be:

  • a native type like :char, :int, :short, …
  • a subclass of FFI::Struct

and num is the number of the elements of the array.

I made some changes on your script and I pasted a working version here:

Keep in mind that the script is working on my system (Ubuntu 32bit) but
might not work on yours. Indeed, as you can see looking at the C header
file “/usr/include/bits/utmpx.h”, there are a bunch of precompiler
conditional branches that made structs definitions platform-dependent.

Regards,
Andrea

On Aug 9, 2:54 am, Andrea F. [email protected] wrote:

Daniel B. wrote:

Info.getstuff # Null Pointer object

Your script runs smoothly on my system (Ubuntu Linux) returning a proper
NativePointer object. It seems that the issue is related to your
environment rather then FFI.

Ah, yes. I was on OS X. I tried again on Linux.

The Utmpx class will be a subclass of FFI::Struct that mirrors the
layout of the C counterpart. Moreover, as I can see from libc reference
manual[1], utmpx has nested structs within. The good news is that
Ruby-FFI has support for nested structs. The bad one is that you have to
define all the nested structs by hand:

class Pid < FFI::Struct
layout …
end

Looks like FFI has a :pid_t type.

I tried this but I’m still having trouble. Part of the problem may be
that I’m not sure how to declare char[] struct members. The docs
indicate there’s a :char_array type, but FFI doesn’t seem to like it.

require ‘ffi’

class Info
extend FFI::Library

BOOT_TIME = 2

attach_function :setutxent, [], :void
attach_function :getutxent, [], :pointer
attach_function :endutxent, [], :void

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

class Utmpx < FFI::Struct
layout(
:ut_user, :char, 32
:ut_id, :char, 4
:ut_line, :char, 32
:ut_pid, :pid_t,
:ut_type, :short,
:ut_tv, Timeval
)
end

def self.getstuff
time = nil

  begin
     setutxent()
     while ent = Utmpx.new(getutxent())
        if ent[:ut_type] == BOOT_TIME
           time = Time.new(ent[:ut_tv][:tv_sec], ent[:ut_tv]

[:tv_usec])
break
end
end
ensure
endutxent()
end

  time

end
end

Info.getstuff

ruby boot_time.rb
boot_time.rb:33:in []': NULL Pointer access attempted (FFI::NullPointerError) from boot_time.rb:33:in getstuff’
from boot_time.rb:46

Where line 33 is the ent[:ut_type] reference.

Any suggestions?

Regards,

Dan