Error handling and errno
Most of the system call functions in Linux and other UNIX-like systems set a special variable called errno when an error occurs. This way, we get a general error code from the return value (often -1) and then more specific information about what went wrong by looking at the errno
variable.
In this recipe, we'll learn what errno
is, how to read values from it, and when it is set. We'll also see an example use case of errno
. Learning about errno
is imperative to system programming, primarily since it's used in conjunction with system calls.
The next few recipes in this chapter are closely tied to this recipe. In this recipe, we'll learn about errno
; in the following three recipes, we'll learn how to interpret the error codes we get from errno
and print human-readable error messages.
Getting ready
You'll need the same components for this recipe that we used in the previous one; that is, the GCC compiler, the Make tool, and the POSIX Programmer's Manual, all of which we have already installed. If not, see Chapter 1, Getting the Necessary Tools and Writing Our First Linux Programs, and the Getting information about Linux- and UNIX-specific header files section of Chapter 3, Diving Deep into C in Linux.
How to do it…
In this recipe, we'll continue building on simple-touch-v2.c
from the first recipe in this chapter. Here, we'll extend it so that it prints some more useful information if it can't create a file:
- Write the following code into a file and save it as
simple-touch-v3.c
. In this version, we'll use theerrno
variable to check if the error is caused by a permission error (EACCES
) or some other, unknown error. The changed code has been highlighted here:#include <stdio.h> #include <fcntl.h> #include <string.h> #include <errno.h> #include <linux/limits.h> int main(int argc, char *argv[]) {    char filename[PATH_MAX] = { 0 };    if (argc != 2)    {       fprintf(stderr, "You must supply a filename "          "as an argument\n");       return 1;    }    strncpy(filename, argv[1], sizeof(filename)-1);    if ( creat(filename, 00644) == -1 )    {       fprintf(stderr, "Can't create file %s\n",          filename);       if (errno == EACCES)       {          fprintf(stderr, "Permission denied\n");       }       else       {          fprintf(stderr, "Unknown error\n");       }       return 1;    }    return 0; }
- Let's compile this version:
$> make simple-touch-v3 gcc -Wall -Wextra -pedantic -std=c99    simple-touch-v3.c   -o simple-touch-v3
- Finally, let's run the new version. This time, the program gives us more information about what went wrong. If it's a permission error, it will tell us that. Otherwise, it will print
Unknown error
:$> ./simple-touch-v3 asdf $> ls -l asdf -rw-r--r-- 1 jake jake 0 okt 13 23:30 asdf $> ./simple-touch-v3 /asdf Can't create file /asdf Permission denied $> ./simple-touch-v3 /non-existent-dir/hello Can't create file /non-existent-dir/hello Unknown error
How it works…
The first difference we'll notice in this version is that we now include a header file called errno.h
. This file is required if we wish to use the errno
variable and the many error macros. One of these macros is EACCES
, which we used in our new version.
The next difference is that we now use sizeof(filename)-1
instead of PATH_MAX-1
for the size argument to strncpy()
. This was something we learned in the previous recipe.
Then, we have the if (errno == EACCES)
line, which checks the errno
variable for EACCES
. We can read about these macros, such as EACCES
, in both man errno.h
and man 2 creat
. This particular macro means permission denied.
When we use errno
, we should first check the return value from the function or system call, as we did here with the if
statement around creat()
. The errno
variable is just like any other variable, meaning that it isn't cleared after the system call. If we were to check errno
directly, before checking the function's return value, errno
could contain an error code from a previous error.
In our version of touch
, we only handle this specific error. Next, we have an else
statement, which catches all other errors and prints an Unknown error
message.
In Step 3, we generated an Unknown error
message by trying to create a file in a directory that doesn't exist on our system. In the next recipe, we'll extend our program so that it can take more macros into account.