3

tl;dr

this works with the GNU version of libc (haven't tried it with uclibc yet)

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65),
                 ('domain', c_char * 65) ]

gnar = uts_struct()

libc.uname(byref(gnar))

print gnar.nodename

Original post

The following code segfaults; I'm not sure what I'm doing wrong.

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

utsname = uts_struct()
libc.uname(byref(utsname))

print utsname.sysname

This does the same thing:

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

utsname = uts_struct()
utsname_pointer = pointer(utsname)
libc.uname(utsname_pointer)

print utsname.sysname

I must be messing up something basic...

(I am aware of os.uname(), this is just an exercise in understanding, which I am failing)

I referenced the uname manual here: http://www.cl.cam.ac.uk/cgi-bin/manpage?2+uname

What am I doing wrong?


Edit:

Thanks to Nemo I'm able to get the data;

>>> from ctypes import *
>>> libc = CDLL('libc.so.6')
>>> gnar = create_string_buffer(512)
>>> libc.uname(byref(gnar))
0
>>> print gnar.value
Linux
>>> 

However, I'm assuming I'm only getting 'Linux' because the items are NULL delimited, as are regulator strings. Any way to read past the NULL?


Edit2:

Based on Nemos comment, I've tried this- which doesn't work, but I thought it might be a step in the right direction... errors with:

Traceback (most recent call last):
  File "gnar.py", line 18, in <module>
    utsname = uts_struct(gnar)
TypeError: incompatible types, c_char_Array_512 instance instead of c_char_p instance

Is this just un-doable?

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char_p),
                 ('nodename', c_char_p),
                 ('release', c_char_p),
                 ('version', c_char_p),
                 ('machine', c_char_p) ]

gnar = create_string_buffer(512)
libc.uname(byref(gnar))
utsname = uts_struct(gnar)

Edit3: ( im going for the longest post ever... =P )

from ctypes import *
libc = CDLL('libc.so.6')
class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65) ]
gnar = uts_struct()
libc.uname(byref(gnar))
print gnar.machine

This works, however, it segfaults after it prints the value...


Final edit:

The following works- I am of course using the GNU version of libc. (im on an Ubuntu machine) so adding the field for the domain is all it took to stop the segfault. It makes sense in hind sight. :)

from ctypes import *

libc = CDLL('libc.so.6')

class uts_struct(Structure):
    _fields_ = [ ('sysname', c_char * 65),
                 ('nodename', c_char * 65),
                 ('release', c_char * 65),
                 ('version', c_char * 65),
                 ('machine', c_char * 65),
                 ('domain', c_char * 65) ]

gnar = uts_struct()
libc.uname(byref(gnar))
print gnar.nodename
Community
  • 1
  • 1
tMC
  • 18,105
  • 14
  • 62
  • 98

2 Answers2

2

The fields in a utsname structure are not pointers; they are "arrays of unspecified size".

So the strings are packed back-to-back in the structure and null-terminated.

I do not know how to represent this in Python. But I would suggest starting with something other than uname() for your experiments. :-)

[update]

517366245708 decimal is 0x78756E694C, which is ascii for "xuniL". Which actually makes sense on a 64-bit little-endian machine...

But the problem is that you are creating a pointer with c_char_p, then you are passing a pointer to that when you invoke byref(). So uname() is filling in your pointer (not what it points to). And worse, it is putting way more than 8 bytes in there, so your current code is clobbering memory.

You need to figure out how to allocate a block of memory the size of a struct utsname, then pass a pointer to that in to the uname() function, then figure out what offsets inside of that block correspond to which fields in the struct. I am not sure how much of this is even possible with Python...

[second update]

That's better... But now you have to look at an offset within the array. If this is a typical Linux system, each field is 65 bytes (yes, really). So you need to start reading 65 bytes into the string. I do not know whether you can call string.index on this thing...

Nemo
  • 70,042
  • 10
  • 116
  • 153
  • No worries. Most C interfaces are not this bizarre. But whenever one of them needs to return something of variable size, it will probably expect you to allocate the space to hold it. (As a Python programmer, managing memory by hand is the most annoying thing you will encounter in C.) – Nemo Jun 02 '11 at 22:26
  • @Nemo, i updated the question, I am able to get the data, but now need to convert it to a string. I don't think I can use `restype` as the function isn't `return`ing anything can I? – tMC Jun 02 '11 at 22:29
  • @Nemo Alright- I think the `create_string_buffer` will allocate the memory? I edited the question again. – tMC Jun 02 '11 at 23:07
  • @Nemo 65 bytes for each field? does that means for the expected 5 items I need to create a buffer of 330 bytes? – tMC Jun 02 '11 at 23:26
  • Well, on my system it's 390... So I guess there is some padding between struct fields going on here. Seriously, uname() is quite possibly the worst C function to be experimenting with here :-) – Nemo Jun 02 '11 at 23:30
  • LOL- alright; im about to give up on it. just figured if i could figure this out, there wouldn't be foreign function i couldnt use :p. +1 for the energy and info – tMC Jun 02 '11 at 23:35
  • @Nemo i gave it one last try; last edit. It doesn't work- I'm tossing in the towel. – tMC Jun 02 '11 at 23:58
2

According to this uname man page, The structure contains arrays of implementation-defined size, not char* (c_char_p). Have you looked at the structure definition in sys/utsname.h? You have to match the exact structure definition. The data type should probably be c_char * n where n is the size of the array from that field found in sys/utsname.h.

Alternatively, you should be able to access all the strings in your first Edit by using print gnar.raw, as long as the buffer is big enough for the whole structure.

Mark Tolonen
  • 166,664
  • 26
  • 169
  • 251
  • `.raw` does return the entire buffer! thank you! (not sure how i managed to miss the `raw` property. I will tinker with the struct and see if it turns out to be usable; by me. :p – tMC Jun 03 '11 at 01:22
  • I added ANOTHER edit to the post- With your suggestion, it seems to work, but is segfaults after printing... This is the closest i've been! – tMC Jun 03 '11 at 01:42