2

I was recently looking at some C code and 'translating' to Python but got stuck at a particular function called _IOR. It is defined in sys/ioctl.h like so:

#define _IOC(inout,group,num,len) \
    (inout | ((len & IOCPARM_MASK) << 16) | ((group) << 8) | (num))
#define _IOR(g,n,t) _IOC(IOC_OUT,   (g), (n), sizeof(t))

And I have seen it called in these ways:

_IOR('t', 3, int)
_IOR('keys', 1, unsigned char *)

The 'keys' call is the one I need to do. Looks like it's doing a bitwise operation on a string.

I managed to find equivalent Python code to the above but it only works for a single character.

_IOC_NRBITS   =  8
_IOC_TYPEBITS =  8
_IOC_SIZEBITS = 14
_IOC_DIRBITS  =  2

_IOC_NRSHIFT = 0
_IOC_TYPESHIFT =(_IOC_NRSHIFT+_IOC_NRBITS)
_IOC_SIZESHIFT =(_IOC_TYPESHIFT+_IOC_TYPEBITS)
_IOC_DIRSHIFT  =(_IOC_SIZESHIFT+_IOC_SIZEBITS)

_IOC_NONE = 0
_IOC_WRITE = 1
_IOC_READ = 2
def _IOC(direction,type,nr,size):
    return (((direction)  << _IOC_DIRSHIFT) |
        ((type) << _IOC_TYPESHIFT) |
        ((nr)   << _IOC_NRSHIFT) |
        ((size) << _IOC_SIZESHIFT))
def _IOR(type, number, size):
    return _IOC(_IOC_READ, type, number, size)

This works for single character calls.

_IOR(ord('t'), 3, 1)

But I don't know the equivalent of the second call with 'keys'. Is there a way to do this C call below in Python?

_IOR('keys', 1, unsigned char *)
Community
  • 1
  • 1
Mendhak
  • 8,194
  • 5
  • 47
  • 64
  • In the second example, isn't the macro oring the address of some stack constant 'keys' into your result? It seems like the macro was not built for evaluating char *... – Pyrce Dec 10 '13 at 17:25
  • It's definitely doing it on the string keys though I'm unsure how; it even compiles and runs! Bit of background in the thread I linked to; it's a vendor provided sample: [link](http://stackoverflow.com/questions/20430434/python-how-can-i-read-input-from-a-device-using-ioctl-or-spidev/20430992). It gives me a magic number -444763391 and I'm trying to figure out how rather than hardcoding. – Mendhak Dec 10 '13 at 17:31

2 Answers2

1

'keys' is an integer character constant (containing four characters), the value of which is implementation-defined. You stated that the value returned by the C code _IOR('keys', 1, unsigned char *) is -444763391, which is, as a 4 byte hex number, 0xE57D7301. From that we can conclude that this implementation computes the integer value 0x6B657973 of 'keys' from 'k'<<24|'e'<<16|'y'<<8|'s'. The _IOR value 0xE57D7301 results from the expression

2<<30|4<<16|'keys'<<8|1
:     :     :         :
:     :     :         1 = num n
:     :     'keys' = group g
:     4 = sizeof (unsigned char *)
2 = _IOC_READ

- note that only 8 Bits are reserved for a TYPE (group) value, and this creative use of a 32 bit value makes the TYPE overflow into the SIZE and DIR bits (getting ORed with them) and its most significant byte (resulting from 'k'<<24) even gets shifted out of the int value.

With your Python function you can compute the same value by calling

_IOR(ord('e')<<16|ord('y')<<8|ord('s'), 1, 4)

(since the k gets lost, we can drop it outright).

Armali
  • 18,255
  • 14
  • 57
  • 171
0

The number you're getting from running on a char* isn't guaranteed to be any specific number. The macro is treating the address of the string as an integer when computing _IOR for keys. The result will change depending on where keys lives in the stack. As a result there's really not a good replacement for this functionality in Python.

From the docs for _IOR(g, n, t):

g
Specifies the group that this ioctl type belongs to. This argument must be a nonnegative 8-bit number (that is, in the range 0-255 inclusive). You can pass the value zero (0) to this argument if a new ioctl group is not being defined.

The intended use of the function is to take a byte as the first argument (via character or integer types). That being said, 'keys' was probably getting assigned to some random group. I would pick a group which you think won't be already taken (or go lookup the group it's currently be assigned to in your C code and use that number) in place of keys.

Alternatively you could always just take the first character of the input string and treat that as the group id.

def _IOR(group, number, size):
    if isinstance(group, basestring):
        group = ord(group[0]) if group else 0
    return _IOC(_IOC_READ, group, number, size)

Then _IOR('t', 3, 1) and _IOR('keys', 1, 1) would both still run, though the group assignments won't be identical to the C macro. But since the macro was being misused for the keys assignment I don't think this will be a problem.

Pyrce
  • 8,296
  • 3
  • 31
  • 46
  • The basic error in this answer is the regarding of `'keys'` as a string, which it isn't; it rather is _an integer character constant containing more than one character_. – Armali Nov 09 '16 at 13:08