前回使った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 変換テーブルが一杯である
コメント