Mailing List Archive

ioctl in python
is there some magic to the fcntl.ioctl() call?

I can do these ioctl calls in C no problem (with a small wrapper):
(amackay@phat)~/tmp$ ./ioctltest /tmp 0x80047601
0x80047601: 1
(amackay@phat)~/tmp$ ./ioctltest / 0x80047601
0x80047601: 0
(amackay@phat)~/tmp$ ./ioctltest /proc 0x80047601
/proc: Inappropriate ioctl for device

that was the Linux ioctl for EXT2 version.

but in python:
>>> a = 0
>>> req = 0x80047601
>>> import fcntl
>>> fo=open('/tmp')
>>> fcntl.ioctl(fo.fileno(), req, a)
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (14, 'Bad address')
>>>

thanks in advance.
cheers, Angus.
ioctl in python [ In reply to ]
From: Angus MacKay <amackay@starvision.com>

well it seems I have sort of solved my problem. if I use a
string for the 3rd arg then it works properly:
>>> fo=open('/tmp')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
'\001\000'
>>> fo=open('/')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
'\000\000'
>>> fo=open('/proc')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (25, 'Inappropriate ioctl for device')

the question is why? it will still fail with only 2 args or an int:
>>> fcntl.ioctl(fo.fileno(), 0x80047601)
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (14, 'Bad address')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, 1)
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (14, 'Bad address')

cheers, Angus.

Angus MacKay wrote:
>
> is there some magic to the fcntl.ioctl() call?
>
> I can do these ioctl calls in C no problem (with a small wrapper):
> (amackay@phat)~/tmp$ ./ioctltest /tmp 0x80047601
> 0x80047601: 1
> (amackay@phat)~/tmp$ ./ioctltest / 0x80047601
> 0x80047601: 0
> (amackay@phat)~/tmp$ ./ioctltest /proc 0x80047601
> /proc: Inappropriate ioctl for device
>
> that was the Linux ioctl for EXT2 version.
>
> but in python:
> >>> a = 0
> >>> req = 0x80047601
> >>> import fcntl
> >>> fo=open('/tmp')
> >>> fcntl.ioctl(fo.fileno(), req, a)
> Traceback (innermost last):
> File "<stdin>", line 1, in ?
> IOError: (14, 'Bad address')
> >>>
ioctl in python [ In reply to ]
well it seems I have sort of solved my problem. if I use a
string for the 3rd arg then it works properly:
>>> fo=open('/tmp')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
'\001\000'
>>> fo=open('/')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
'\000\000'
>>> fo=open('/proc')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (25, 'Inappropriate ioctl for device')

the question is why? it will still fail with only 2 args or an int:
>>> fcntl.ioctl(fo.fileno(), 0x80047601)
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (14, 'Bad address')
>>> fcntl.ioctl(fo.fileno(), 0x80047601, 1)
Traceback (innermost last):
File "<stdin>", line 1, in ?
IOError: (14, 'Bad address')

cheers, Angus.

Angus MacKay wrote:
>
> is there some magic to the fcntl.ioctl() call?
>
> I can do these ioctl calls in C no problem (with a small wrapper):
> (amackay@phat)~/tmp$ ./ioctltest /tmp 0x80047601
> 0x80047601: 1
> (amackay@phat)~/tmp$ ./ioctltest / 0x80047601
> 0x80047601: 0
> (amackay@phat)~/tmp$ ./ioctltest /proc 0x80047601
> /proc: Inappropriate ioctl for device
>
> that was the Linux ioctl for EXT2 version.
>
> but in python:
> >>> a = 0
> >>> req = 0x80047601
> >>> import fcntl
> >>> fo=open('/tmp')
> >>> fcntl.ioctl(fo.fileno(), req, a)
> Traceback (innermost last):
> File "<stdin>", line 1, in ?
> IOError: (14, 'Bad address')
> >>>
ioctl in python [ In reply to ]
Hi Angus,

The ioctl cmd code (0x80047601) is expecting a 4 byte buffer to be
passed to it. The
simplest thing is something like :

rec = pack('BBBB', 0,0,0,0)
or
rec = pack('L', 0)

depending on what's expected, then

ioctl(handle, 0x80047601, rec)

By the way, the 80 part of the command also says this is an input
function, so you
are writing the data, rec in this case! For an output function you would
follow the
ioctl with an unpack of the data buffer, as well. But you still need to
supply the
packed input buffer in any case.

Hope this helps!

Regards,
Perry

Angus MacKay wrote:

> well it seems I have sort of solved my problem. if I use a
> string for the 3rd arg then it works properly:
> >>> fo=open('/tmp')
> >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> '\001\000'
> >>> fo=open('/')
> >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> '\000\000'
> >>> fo=open('/proc')
> >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> Traceback (innermost last):
> File "<stdin>", line 1, in ?
> IOError: (25, 'Inappropriate ioctl for device')
>
> the question is why? it will still fail with only 2 args or an int:
> >>> fcntl.ioctl(fo.fileno(), 0x80047601)
> Traceback (innermost last):
> File "<stdin>", line 1, in ?
> IOError: (14, 'Bad address')
> >>> fcntl.ioctl(fo.fileno(), 0x80047601, 1)
> Traceback (innermost last):
> File "<stdin>", line 1, in ?
> IOError: (14, 'Bad address')
>
> cheers, Angus.
>
> Angus MacKay wrote:
> >
> > is there some magic to the fcntl.ioctl() call?
> >
> > I can do these ioctl calls in C no problem (with a small wrapper):
> > (amackay@phat)~/tmp$ ./ioctltest /tmp 0x80047601
> > 0x80047601: 1
> > (amackay@phat)~/tmp$ ./ioctltest / 0x80047601
> > 0x80047601: 0
> > (amackay@phat)~/tmp$ ./ioctltest /proc 0x80047601
> > /proc: Inappropriate ioctl for device
> >
> > that was the Linux ioctl for EXT2 version.
> >
> > but in python:
> > >>> a = 0
> > >>> req = 0x80047601
> > >>> import fcntl
> > >>> fo=open('/tmp')
> > >>> fcntl.ioctl(fo.fileno(), req, a)
> > Traceback (innermost last):
> > File "<stdin>", line 1, in ?
> > IOError: (14, 'Bad address')
> > >>>
ioctl in python [ In reply to ]
hun?

why is this expecting a 4 byte buffer? my C version uses an int*
and it works no problem.

as for 80 being a input I think you have it backwards (for linux-i386 at least):
0x80046601 EXT2_IOC_GETFLAGS int *
0x40046602 EXT2_IOC_SETFLAGS const int *
0x80047601 EXT2_IOC_GETVERSION int *
0x40047602 EXT2_IOC_SETVERSION const int *

cheers, Angus.

Perry Faulkner wrote:
>
> Hi Angus,
>
> The ioctl cmd code (0x80047601) is expecting a 4 byte buffer to be
> passed to it. The
> simplest thing is something like :
>
> rec = pack('BBBB', 0,0,0,0)
> or
> rec = pack('L', 0)
>
> depending on what's expected, then
>
> ioctl(handle, 0x80047601, rec)
>
> By the way, the 80 part of the command also says this is an input
> function, so you
> are writing the data, rec in this case! For an output function you would
> follow the
> ioctl with an unpack of the data buffer, as well. But you still need to
> supply the
> packed input buffer in any case.
>
> Hope this helps!
>
> Regards,
> Perry
>
> Angus MacKay wrote:
>
> > well it seems I have sort of solved my problem. if I use a
> > string for the 3rd arg then it works properly:
> > >>> fo=open('/tmp')
> > >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> > '\001\000'
> > >>> fo=open('/')
> > >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> > '\000\000'
> > >>> fo=open('/proc')
> > >>> fcntl.ioctl(fo.fileno(), 0x80047601, "hi")
> > Traceback (innermost last):
> > File "<stdin>", line 1, in ?
> > IOError: (25, 'Inappropriate ioctl for device')
> >
> > the question is why? it will still fail with only 2 args or an int:
> > >>> fcntl.ioctl(fo.fileno(), 0x80047601)
> > Traceback (innermost last):
> > File "<stdin>", line 1, in ?
> > IOError: (14, 'Bad address')
> > >>> fcntl.ioctl(fo.fileno(), 0x80047601, 1)
> > Traceback (innermost last):
> > File "<stdin>", line 1, in ?
> > IOError: (14, 'Bad address')
> >
> > cheers, Angus.
> >
> > Angus MacKay wrote:
> > >
> > > is there some magic to the fcntl.ioctl() call?
> > >
> > > I can do these ioctl calls in C no problem (with a small wrapper):
> > > (amackay@phat)~/tmp$ ./ioctltest /tmp 0x80047601
> > > 0x80047601: 1
> > > (amackay@phat)~/tmp$ ./ioctltest / 0x80047601
> > > 0x80047601: 0
> > > (amackay@phat)~/tmp$ ./ioctltest /proc 0x80047601
> > > /proc: Inappropriate ioctl for device
> > >
> > > that was the Linux ioctl for EXT2 version.
> > >
> > > but in python:
> > > >>> a = 0
> > > >>> req = 0x80047601
> > > >>> import fcntl
> > > >>> fo=open('/tmp')
> > > >>> fcntl.ioctl(fo.fileno(), req, a)
> > > Traceback (innermost last):
> > > File "<stdin>", line 1, in ?
> > > IOError: (14, 'Bad address')
> > > >>>
ioctl in python [ In reply to ]
Angus MacKay <amackay@starvision.com> wrote:
: hun?

: why is this expecting a 4 byte buffer? my C version uses an int*
: and it works no problem.

: as for 80 being a input I think you have it backwards (for linux-i386 at least):
: 0x80046601 EXT2_IOC_GETFLAGS int *
: 0x40046602 EXT2_IOC_SETFLAGS const int *
: 0x80047601 EXT2_IOC_GETVERSION int *
: 0x40047602 EXT2_IOC_SETVERSION const int *

: cheers, Angus.

The four bytes is the sizeof(int *), but also (according to some linux
headers I was looking at) it is expecting a pointer to a long, not an
int. A long is a 4 byte value (not to mention that an int is also a
long on most systems).

But, the ioctl call takes some 4 byte value, it could be cast from a
char, short, pointer or whatever; it is up to the called request to
interpret the value (int *, in this case) given. In your original
(Python) program, you gave ioctl() a fairly short character string
(three bytes if I remember correctly), which would not be the size of a
long.

-Arcege
ioctl in python [ In reply to ]
From: "Michael P. Reilly" <arcege@shore.net>

Angus MacKay <amackay@starvision.com> wrote:
: hun?

: why is this expecting a 4 byte buffer? my C version uses an int*
: and it works no problem.

: as for 80 being a input I think you have it backwards (for linux-i386 at
least):
: 0x80046601 EXT2_IOC_GETFLAGS int *
: 0x40046602 EXT2_IOC_SETFLAGS const int *
: 0x80047601 EXT2_IOC_GETVERSION int *
: 0x40047602 EXT2_IOC_SETVERSION const int *

: cheers, Angus.

The four bytes is the sizeof(int *), but also (according to some linux
headers I was looking at) it is expecting a pointer to a long, not an
int. A long is a 4 byte value (not to mention that an int is also a
long on most systems).

But, the ioctl call takes some 4 byte value, it could be cast from a
char, short, pointer or whatever; it is up to the called request to
interpret the value (int *, in this case) given. In your original
(Python) program, you gave ioctl() a fairly short character string
(three bytes if I remember correctly), which would not be the size of a
long.

-Arcege