开发者

Calling uname from libc with Pythons ctypes

开发者 https://www.devze.com 2023-03-10 06:04 出处:网络
tl;dr this works with the GNU version of libc (haven\'t tried it with uclibc yet) from ctypes import *

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


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消