Linuxでは ioctlは

/usr/include/sys/ioctl.h:
extern int ioctl (int __fd, unsigned long int __request, ...) __THROW;

というプロトタイプになっているんだけど、なぜか manでは

int ioctl(int d, int request, ...);

と書いてあり、一致していないという問題があった。
実装がBSD互換で、manがOpenGroupのSUS準拠だったわけだね。

わたしがmanの問題だとおもって2012年1月にmanにチケットきったんだけどほうちされてて、

https://bugzilla.kernel.org/show_bug.cgi?id=42705

2012年7月にはLinusが glibcの実装の方を直すべきと主張して、glibcのほうにチケットを発行、

http://sourceware.org/bugzilla/show_bug.cgi?id=14362

どうなるのかやきもきしていたが、Michael 的にはmanは実装と一致すべきと思ったのだろう、
manを変更したとの連絡がとどいた。

これで使用法誤解がへるといいな。

以下、技術的および歴史的な状況の解説

・当初、ioctlはPOSIXに含まれていなかったのでBSD系は引数がunsigned long, SYSV系がintと分裂してた。
・glibcは、すごく昔の段階で多数派のBSDに日和った
・STREAMSをPOSIXに含めるときにioctlがPOSIXに追加された。しかし、それは(規格上は)STREAMS専用インタフェースで他の用途はしらんがな状態。かつ最新の規格ではSTREAMSはすでにobsolete
・世間の大多数のソフトは引数をintだと決め打っている。Macに至ってはカーネルが(BSD互換なので)unsigned longなのに、システムコンポーネントが int だと思って処理しているような箇所があり、

int action = (level ? TIOCSBRK : TIOCCBRK);
...
ioctl(fd, action, ...);

というコードが符号拡張で0xFFFFFFFF.... となってしまって、常に ENOTTY になってしまう有り様だった(Linusいわく)

・Linuxでは、そういうアホなユーザーランドへの対策として、64bitでも上位32bitは無視しているのでこの問題はおきない。たとえ、同様のミスをユーザーランドがしたとしても
・これをもって、Linusはカーネルはintでいいようになっていると主張していたが、manのほうのチケットでDavidが指摘したように、実際には最上位ビットも使っているので unsigned int 相当の動きをしている(Linusは暗黙の変換でうまくいくと主張していたが、まあ、そこはそれ)
・Linus的には 「So please fix the ioctl() declaration. "unsigned long" is misleading and actively incorrect and can cause bugs on non-Linux operating systems.」とglibcが直したほうが、よそのOSでのバグがなくなるんだと主張していたが、glibc関係者からは「Macにアホなバグがるから、glibcを直せってのはロジックが無茶苦茶だ」という反発がじゃっかんあったりなかったり。

とかなんとか迷走していた。
なお余談だが、Rubyもまったく同じバグが最近まであったのだが、1.9.3でわたしが直した。

備忘録なので、このエントリに特に結論はない。