I am using a system call and in case it fails, I need to do different things for different errnos.
I need to write code that looks something like this:
I assume you are using Linux, and I suppose that you don't directly use the system call, but some of the (simple) wrappers (from your C library) listed in syscalls(2). Notice that some weird system calls are not wrapped by the C library (a well known example of unwrapped system call would be sigreturn(2) which you probably should never use). Quite often the C library is GNU glibc, but it might be musl-libc etc. Notice also that kernel raw system calls have different calling conventions than ordinary C function (so in practice a libc wrapper is required, and is in charge of dealing with errno
). Notice also that errno(3) is generally a macro (almost behaving as some variable).
The msgrcv(2) man page documents that errno
could be one of E2BIG
, EACCES
, EFAULT
... ENOMSG
, ENOSYS
... (refer to that man page to get the list of all possible errors).
So you would code something like
ssize_t siz = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg);
if (siz<0) { // msgrcv failed and has set errno
if (errno == ENOMSG)
dosomething();
else if (errno == EAGAIN)
dosomethingelse();
/// etc
else {
syslog(LOG_DAEMON|LOG_ERR, "msgrcv failure with %s\n",
strerror(errno));
exit(EXIT_FAILURE);
};
};
Is the statement
if (errno == ENOMSG)
.... valid?
Yes it is; you want to test errno
only after some system call failure (e.g. when siz<0
).
Is there such a variable
errno
?
Not any more. Please read carefully errno(3) documentation. You should not declare extern int errno;
(this was possible in the 1980s, not in 21st century) but you should always #include <errno.h>
and use errno
as if it was a variable, but it is almost always some macro (whose definition appears in /usr/include/bits/errno.h
which is included by /usr/include/errno.h
).
BTW, SysV-style facilities tend to become obsolete and are not always available. I recommend using POSIX message queues facilities, read mq_overview(7).
You might want to read the freely downloadable Advanced Linux Programming (an old book; you can buy something better & newer) and/or all the man pages reachable from intro(2) & syscalls(2) & intro(3).
How to check the value of errno
:
#include <errno.h>
.if(errno == ENOENT) { ... }
, and that is the common and recommended way of doing it.errno
to determine that an error has occurred. Check the return value of the function, and if the return value indicates an error, then check errno
to see what the error was. (More on this below.)errno
looks like a variable, but it actually isn't. This doesn't concern you as long as you just say things like if(errno == ENOENT) { ... }
. But you probably shouldn't try to do something like int errno_ptr = &errno;
.perror()
and strerror()
to get human-readable error strings corresponding to errno
values. But, yes, the strings you get are generally things like "No such file or directory". There is no good way that I know of to convert the errno value ENOENT
to the string "ENOENT"
.To say a little more about #3. Sometimes it's tempting to say something like
errno = 0;
printf("Hello, world!\n");
if(errno != 0) {
fprintf(stderr, "printf failed!\n");
}
But don't do that. Instead do
errno = 0;
int retval = printf("Hello, world!\n");
if(retval < 0) {
fprintf(stderr, "printf failed!\n");
}
The reason is that, somewhere in the process of doing its job, printf
might have done something that resulted in an error, something that set errno
, but printf
might have recovered from that error and gone on to complete successfully.
There are a very few library functions that are guaranteed not to touch errno if there wasn't an error (I think one example might be atoi
), but in general, this is something you have to be careful of.
To say a little more about #4. errno
looks like a variable, and more specifically, it looks like a global variable. But of course global variables are bad. But errno
has been around forever; there are tens of millions of lines of code that use it; it's still basically pretty convenient; it's too late to "fix" it. So, instead, if you peek behind the curtain, you'll find that most implementations do something like
extern int __errno_pointer;
#define errno (*__errno_pointer)
or
extern int *__errno_pointer_function();
#define errno (*__errno_function())
In this way, they can arrange for errno
to work reasonably properly even in, say, multithreaded code.
Include errno.h
Some examples:
// Error codes
#define EPERM 1 /* Operation not permitted */
#define ENOENT 2 /* No such file or directory */
#define ESRCH 3 /* No such process */
#define EINTR 4 /* Interrupted system call */
#define EIO 5 /* I/O error */
#define ENXIO 6 /* No such device or address */
#define E2BIG 7 /* Argument list too long */
#define ENOEXEC 8 /* Exec format error */
#define EBADF 9 /* Bad file number */
#define ECHILD 10 /* No child processes */
#define EAGAIN 11 /* Try again */
#define ENOMEM 12 /* Out of memory */
#define EACCES 13 /* Permission denied */
#define EFAULT 14 /* Bad address */
#define ENOTBLK 15 /* Block device required */
#define EBUSY 16 /* Device or resource busy */
#define EEXIST 17 /* File exists */
#define EXDEV 18 /* Cross-device link */
#define ENODEV 19 /* No such device */
#define ENOTDIR 20 /* Not a directory */
#define EISDIR 21 /* Is a directory */
#define EINVAL 22 /* Invalid argument */
#define ENFILE 23 /* File table overflow */
#define EMFILE 24 /* Too many open files */
#define ENOTTY 25 /* Not a typewriter */
#define ETXTBSY 26 /* Text file busy */
#define EFBIG 27 /* File too large */
#define ENOSPC 28 /* No space left on device */
#define ESPIPE 29 /* Illegal seek */
#define EROFS 30 /* Read-only file system */
#define EMLINK 31 /* Too many links */
#define EPIPE 32 /* Broken pipe */
#define EDOM 33 /* Math argument out of domain of func */
#define ERANGE 34 /* Math result not representable */
Your implementation may have more errno includes, like for example /usr/include/asm-generic/errno.h
.