Errno and Error Management in C
- Mia Combeau
- C
- November 1, 2022
Table of Contents
Underlying any software development is program error detection and analysis. But then an external library function or system call fails, how can we understand what went wrong? These functions usually return -1 or NULL
, but they also store the reason behind their failure in errno.
Let’s see what errno actually is, and how to retrieve, interpret and print it out.
What is Errno?
Errno (“error number”) is an integer variable stored in the <errno.h>
library. When a system call fails, it systematically places its error code there to explain the conditions surrounding its failure. Many other function of standard libraries also do this. The error code can then be interpreted to understand what went wrong.
At the beginning of a program, errno always starts off with a value of 0, which indicates success. However, once it is modified to contain an error code, it will never be reset to 0. Indeed, no system call or library function ever stores 0 in errno. This is why its value is only significant immediately after a system call or standard function failure. Usually, this is after the call returns -1 or NULL
.
Yet, some functions can return -1 or NULL in case of success as well as failure, like the readdir and getpriority functions. In this case, it is important to reset errno to 0 before the function call to be able to check if an error occurred during the call. Assigning a value to it could not be simpler :
errno = 0;
The numbers stored in errno to represent various error are associates to macros that provide them with more descriptive names. They also have a corresponding short sentence to describe the error in question. For example, error code 13
is associated with the EACCESS
macro, and to the descriptive sentence “Permission denied”.
Errno can contain over a hundred described error codes. So what are all of those error codes? We can take a look at them with the errno command in Unix type systems.
The Errno Command in Unix
The errno command is a useful tool to list or search for error codes. It is available on Unix type systems with the moreutils packet, which can be installed this way if it is not already:
$ sudo apt install moreutils
In order to list all of the error codes as well as their macro name and corresponding descriptions, all we need now do is type the following command:
$ errno -l
EPERM 1 Operation not permitted
ENOENT 2 No such file or directory
ESRCH 3 No such process
EINTR 4 Interrupted system call
EIO 5 Input/output error
ENXIO 6 No such device or address
E2BIG 7 Argument list too long
ENOEXEC 8 Exec format error
EBADF 9 Bad file descriptor
ECHILD 10 No child processes
EAGAIN 11 Resource temporarily unavailable
ENOMEM 12 Cannot allocate memory
EACCES 13 Permission denied
EFAULT 14 Bad address
(...)
We can also search for an error by its numeric code or by its macro name:
$ errno 21
EISDIR 21 Is a directory
$ errno ENOENT
ENOENT 2 No such file or directory
Better yet, we can even search for all errors containing a certain word in their descriptions with the -s
option :
$ errno -s directory
ENOENT 2 No such file or directory
ENOTDIR 20 Not a directory
EISDIR 21 Is a directory
ENOTEMPTY 39 Directory not empty
But wouldn’t it be useful to display the error’s descriptions from our C programs ? Let’s see how to do just that.
Displaying Errno’s Error
There are two functions that allow us to translate the numeric error code in errno into its corresponding descriptive sentence, in order to display it in our terminal: perror
and strerror
.
Displaying the Error with Perror
The perror
function of the <stdio.h>
library has the following prototype:
void perror(const char *s);
Called after an error occurred, the perror
function prints, in the standard error ( file descriptor 2), the given string s, followed by a colon “:” and then by the string corresponding to the current errno value.
For example, let’s attempt to open a file that does not exist and use perror
after we’ve detected the open
functions failure:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("does_not_exist", O_RDONLY);
if (fd == -1)
{
perror("open");
return (1);
}
close(fd);
return (0);
}
Displaying the Error with Strerror
With the strerror
function of the <string.h>
library, we have more flexibility in the way we present the error message since it only converts the errno code into a string. Here is its prototype:
char *strerror(int errnum);
We can simply indicate the errno variable in its errnum parameter to transform the integer into its corresponding string. The strerror
function does not display anything itself, it only returns the appropriate string. With this function, we need to print the error ourselves, preferably to the standard error (fd2).
Let’s once again try to open a non-existant file and print the error with the write
function once we’ve converted errno into a string with strerror
:
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int main(void)
{
int fd;
fd = open("does_not_exist", O_RDONLY);
if (fd == -1)
{
write(2, strerror(errno), strlen(strerror(errno)));
write(2, "\n", 1);
return (1);
}
close(fd);
return (0);
}
A little tip to share, a nagging question to ask, or a strange discovery to discuss about errno or error management in C? I’d love to read and respond to it all in the comments. Happy coding !
Sources and Further Reading
-
Linux Programmer’s Manual:
-
Rentes, M., moreutils: the utilities package every UNIX/Linux/Mac OS developer should know [rentes.github.io]