errnoを使う

前回使ったperrorは自動的に直近のエラーについて表示してくれるので便利でした。
しかし何のエラーだったのかわかりません。エラーの種類を知るには、errnoを確認する必要があります。

takk@deb9:~/tmp$ cat -n test.c
     1	#include <sys/types.h>
     2	#include <sys/stat.h>
     3	#include <unistd.h>
     4	#include <stdio.h>
     5	#include <errno.h>
     6	
     7	int main(int argc, char* argv[])
     8	{
     9		struct stat buf;
    10		char* filename;
    11		filename = argv[1];
    12	
    13		if(stat(filename,&buf) == -1){
    14			int this_errno = errno;
    15			switch(this_errno){
    16			case	ENOENT:
    17				perror(filename);
    18				break;
    19			default:
    20				printf("any other error\n");
    21			}
    22		}
    23		return 0;
    24	}
takk@deb9:~/tmp$ truncate -s5 test.bin
takk@deb9:~/tmp$ ./a.out test.bin
takk@deb9:~/tmp$ 

存在しないファイルをstatした場合は、errnoにENOENTが保存されています。

takk@deb9:~/tmp$ gcc test.c
takk@deb9:~/tmp$ ./a.out a
a: No such file or directory
takk@deb9:~/tmp$ 

test.binがリードできないように権限を変えてみます。

takk@deb9:~/tmp$ sudo chown root.root test.bin
takk@deb9:~/tmp$ sudo chmod 600 test.bin
takk@deb9:~/tmp$ ls -l
合計 16
-rwxr-xr-x 1 takk takk 8856  4月 21 21:24 a.out
-rw------- 1 root root    5  4月 21 21:25 test.bin
-rw-r--r-- 1 takk takk  385  4月 21 21:24 test.c
takk@deb9:~/tmp$ 

実行してみると、

takk@deb9:~/tmp$ ./a.out test.bin
takk@deb9:~/tmp$ 

stat自体が成功してしまいます。
それもそのはず、権限がないとは言っても、statコマンド自体は通るので、同じようにstat関数も成功します。

takk@deb9:~/tmp$ stat test.bin
  File: test.bin
  Size: 5         	Blocks: 0          IO Block: 4096   通常ファイル
Device: 801h/2049d	Inode: 1739551     Links: 1
Access: (0600/-rw-------)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2018-04-21 21:25:01.632001000 +0900
Modify: 2018-04-21 21:25:01.632001000 +0900
Change: 2018-04-21 21:26:51.176001000 +0900
 Birth: -
takk@deb9:~/tmp$ 

次はopenで試します。読み込み権限がない場合は、errnoにEACCESが設定されるので、defaultに落ちるはずです。

takk@deb9:~/tmp$ cat -n test.c
     1	#include <sys/types.h>
     2	#include <sys/stat.h>
     3	#include <unistd.h>
     4	#include <fcntl.h>
     5	#include <stdio.h>
     6	#include <errno.h>
     7	
     8	int main(int argc, char* argv[])
     9	{
    10		char* filename;
    11		int fd;
    12		char buf[1000];
    13		filename = argv[1];
    14	
    15		if(open(filename,O_RDONLY) == -1){
    16			int this_errno = errno;
    17			switch(this_errno){
    18			case	ENOENT:
    19				perror(filename);
    20				break;
    21			default:
    22				printf("any other error\n");
    23			}
    24		}
    25		return 0;
    26	}
takk@deb9:~/tmp$ 

実行します。defaultを通ったようです。

takk@deb9:~/tmp$ gcc test.c
takk@deb9:~/tmp$ ./a.out test.bin
any other error
takk@deb9:~/tmp$ 

EACCES通る時にperrorで処理してくれるように、プログラムを以下のように書き換えて試してみます。

    18			case	ENOENT:
    19				perror(filename);
    20				break;
    21			case	EACCES:
    22				perror(filename);
    23				break;
    24			default:
    25				printf("any other error\n");

よく見なれたエラー文が表示されました。

takk@deb9:~/tmp$ ./a.out test.bin
test.bin: Permission denied
takk@deb9:~/tmp$ 

man 3 errnoを載せておきます。

ERRNO(3)                   Linux Programmer's Manual                  ERRNO(3)

名前
       errno - 直近に発生したエラーの番号

書式
       #include <errno.h>

説明
       ヘッダーファイル <errno.h> で整数型の変数 errno が定義されており、 シス
       テムコールやいくつかのライブラリ関数は、エラーが発生した際に この変数に
       その原因を示す値を設定する。 この値は呼び出しの返り値がエラー (ほとんど
       のシステムコールでは -1 で、ほとんどのライブラリ関数では -1 か NULL) を
       示したときに  のみ意味を持つが、ライブラリ関数は成功した場合も errno を
       変更することが許されている。

       有効なエラー番号はいずれも 0 以外の値を持つ。  どのシステムコールもライ
       ブラリ関数も errno を 0 に設定することはない。

       いくつかのシステムコールやライブラリ関数  (例えば  getpriority(2))   で
       は、成功した場合の有効な返り値として -1 が返されることがある。 このよう
       な場合、成功なのかエラーなのかを区別するためには、 呼び出しの前に errno
       を 0 に設定しておけばよい。呼び出しの返り値がエラー発生の可能性を  示す
       ものだった場合には、 errno が 0 以外の値かを見て確認すればよい。

       errno は、ISO C standard で int 型の変更可能な左辺値 として定義されてお
       り、明示的に宣言を行ってはならない;  errno   はマクロの場合もありえる。
       errno  はスレッド毎に値を持つ。 つまりあるスレッドで errno が設定されて
       も、 他のスレッドの errno には影響しない。

       POSIX.1 で定義されているすべてのエラー名には、 それぞれ異なる値が対応し
       ていなければならない。  但し、 EAGAIN と EWOULDBLOCK は例外で、これらは
       同じ値を持ってもよい。

       Below is a list of the symbolic error names that are defined on  Linux.
       Some  of  these are marked POSIX.1, indicating that the name is defined
       by POSIX.1-2001, or C99, indicating that the name is defined by C99.

       E2BIG           引き数リストが長過ぎる (POSIX.1)

       EACCES          許可がない (POSIX.1)

       EADDRINUSE      アドレスがすでに使用されている (POSIX.1)

       EADDRNOTAVAIL   アドレスが使用できない (POSIX.1)

       EAFNOSUPPORT    アドレスファミリーがサポートされていない (POSIX.1)

       EAGAIN          リソースが一時的に利用不可 (EWOULDBLOCK  と同じ値でもよ
                       い) (POSIX.1)

       EALREADY        接続が既に処理中である (POSIX.1)

       EBADE           不正なやり取り (exchange) である

       EBADF           ファイルディスクリプターが不正である (POSIX.1)

       EBADFD          ファイルディスクリプターが不正な状態である

       EBADMSG         メッセージが不正である (POSIX.1)

       EBADR           不正なリクエストディスクリプター

       EBADRQC         不正なリクエストコード

       EBADSLT         不正なスロット

       EBUSY           リソースが使用中である (POSIX.1)

       ECANCELED       操作がキャンセルされた (POSIX.1)

       ECHILD          子プロセスが無い (POSIX.1)

       ECHRNG          チャンネル番号が範囲外である

       ECOMM           送信時に通信エラーが発生した

       ECONNABORTED    接続が中止された (POSIX.1)

       ECONNREFUSED    接続が拒否された (POSIX.1)

       ECONNRESET      接続がリセットされた (POSIX.1)

       EDEADLK         リソースのデッドロックを回避した (POSIX.1)

       EDEADLOCK       EDEADLK の同義語

       EDESTADDRREQ    宛先アドレスが必要である (POSIX.1)

       EDOM            数学関数で引き数が領域外である (out of domain)

       EDQUOT          ディスククォータ (quota) を超過した (POSIX.1)

       EEXIST          ファイルが存在する (POSIX.1)

       EFAULT          アドレスが不正である (POSIX.1)

       EFBIG           ファイルが大き過ぎる (POSIX.1)

       EHOSTDOWN       ホストがダウンしている

       EHOSTUNREACH    ホストに到達不能である (POSIX.1)

       EIDRM           識別子が削除された (POSIX.1)

       EILSEQ          不正なバイト列 (POSIX.1, C99)

       EINPROGRESS     操作が実行中である (POSIX.1)

       EINTR           関数呼び出しが割り込まれた (POSIX.1); signal(7)  参照。

       EINVAL          引数が無効である (POSIX.1)

       EIO             入出力エラー (POSIX.1)

       EISCONN         ソケットが接続されている (POSIX.1)

       EISDIR          ディレクトリである (POSIX.1)

       EISNAM          名前付きのファイルである

       EKEYEXPIRED     鍵が期限切れとなった

       EKEYREJECTED    鍵がサーバにより拒否された

       EKEYREVOKED     鍵が無効となった

       EL2HLT          停止 (レベル 2)

       EL2NSYNC        同期できていない (レベル 2)

       EL3HLT          停止 (レベル 3)

       EL3RST          停止 (レベル 3)

       ELIBACC         必要な共有ライブラリにアクセスできなかった

       ELIBBAD         壊れた共有ライブラリにアクセスしようとした

       ELIBMAX         リンクしようとした共有ライブラリが多過ぎる

       ELIBSCN         a.out のライブラリセクションが壊れている (corrupted)

       ELIBEXEC        共有ライブラリを直接実行できなかった

       ELOOP           シンボリックリンクの回数が多過ぎる (POSIX.1)

       EMEDIUMTYPE     間違ったメディア種別である

       EMFILE          オープンしているファイルが多過ぎる  (POSIX.1)。  通常は
                       getrlimit(2) に説明があるリソース上限 RLIMIT_NOFILE  を
                       超過した場合に発生する。

       EMLINK          リンクが多過ぎる (POSIX.1)

       EMSGSIZE        メッセージが長過ぎる (POSIX.1)

       EMULTIHOP       マルチホップ (multihop) を試みた (POSIX.1)

       ENAMETOOLONG    ファイル名が長過ぎる (POSIX.1)

       ENETDOWN        ネットワークが不通である (POSIX.1)

       ENETRESET       接続がネットワーク側から中止された (POSIX.1)

       ENETUNREACH     ネットワークが到達不能である (POSIX.1)

       ENFILE          システム全体でオープンされているファイルが多過ぎる
                       (POSIX.1)

       ENOBUFS         使用可能なバッファー空間がない  (POSIX.1  (XSI  STREAMS
                       option))

       ENODATA         ストリームの読み出しキューの先頭に読み出し可能なメッ
                       セージがない (POSIX.1)

       ENODEV          そのようなデバイスは無い (POSIX.1)

       ENOENT          そのようなファイルやディレクトリは無い (POSIX.1)

       ENOEXEC         実行ファイル形式のエラー (POSIX.1)

       ENOKEY          要求された鍵が利用できない

       ENOLCK          利用できるロックが無い (POSIX.1)

       ENOLINK         リンクが切れている (POSIX.1)

       ENOMEDIUM       メディアが見つからない

       ENOMEM          十分な空きメモリー領域が無い (POSIX.1)

       ENOMSG          要求された型のメッセージが存在しない (POSIX.1)

       ENONET          マシンがネットワーク上にない

       ENOPKG          パッケージがインストールされていない

       ENOPROTOOPT     指定されたプロトコルが利用できない (POSIX.1)

       ENOSPC          デバイスに空き領域が無い (POSIX.1)

       ENOSR           指定されたストリームリソースが存在しない (POSIX.1  (XSI
                       STREAMS option))

       ENOSTR          ストリームではない (POSIX.1 (XSI STREAMS option))

       ENOSYS          関数が実装されていない (POSIX.1)

       ENOTBLK         ブロックデバイスが必要である

       ENOTCONN        ソケットが接続されていない (POSIX.1)

       ENOTDIR         ディレクトリではない (POSIX.1)

       ENOTEMPTY       ディレクトリが空ではない (POSIX.1)

       ENOTSOCK        ソケットではない (POSIX.1)

       ENOTSUP         操作がサポートされていない (POSIX.1)

       ENOTTY          I/O 制御操作が適切でない (POSIX.1)

       ENOTUNIQ        名前がネットワークで一意ではない

       ENXIO           そのようなデバイスやアドレスはない (POSIX.1)

       EOPNOTSUPP      ソケットでサポートしていない操作である (POSIX.1)

                       (Linux  では  ENOTSUP  と EOPNOTSUPP は同じ値を持つが、
                       POSIX.1 に従えば両者のエラー値は区別されるべきである。)

       EOVERFLOW       指定されたデータ型に格納するには値が大き過ぎる
                       (POSIX.1)

       EPERM           操作が許可されていない (POSIX.1)

       EPFNOSUPPORT    サポートされていないプロトコルファミリーである

       EPIPE           パイプが壊れている (POSIX.1)

       EPROTO          プロトコルエラー (POSIX.1)

       EPROTONOSUPPORT プロトコルがサポートされていない (POSIX.1)

       EPROTOTYPE      ソケットに指定できないプロトコルタイプである (POSIX.1)

       ERANGE          結果が大き過ぎる (POSIX.1, C99)

       EREMCHG         リモートアドレスが変わった

       EREMOTE         オブジェクトがリモートにある

       EREMOTEIO       リモート I/O エラー

       ERESTART        システムコールが中断され再スタートが必要である

       EROFS           読み出し専用のファイルシステムである (POSIX.1)

       ESHUTDOWN       通信相手がシャットダウンされて送信できない

       ESPIPE          無効なシーク (POSIX.1)

       ESOCKTNOSUPPORT サポートされていないソケット種別である

       ESRCH           そのようなプロセスは無い (POSIX.1)

       ESTALE          ファイルハンドルが古い状態になっている (POSIX.1)

                       NFS や他のファイルシステムで起こりうる。

       ESTRPIPE        ストリームパイプエラー

       ETIME           時間が経過した (POSIX.1 (XSI STREAMS option))

                       (POSIX.1  では  "STREAM ioctl(2)  timeout" と書かれてい
                       る)

       ETIMEDOUT       操作がタイムアウトした (POSIX.1)

       ETXTBSY         テキストファイルが使用中である (POSIX.1)

       EUCLEAN         Structure needs cleaning

       EUNATCH         プロトコルのドライバが付与 (attach) されていない

       EUSERS          ユーザー数が多過ぎる

       EWOULDBLOCK     操作がブロックされる見込みである (EAGAIN と同じ値でもよ
                       い) (POSIX.1)

       EXDEV           不適切なリンク (POSIX.1)

       EXFULL          変換テーブルが一杯である

コメント

タイトルとURLをコピーしました