Checking if errno != EINTR: what does it mean? - c

I've found this piece of code used several times (also a similar one where it's used open() instead of write()).
int c = write(fd, &v, sizeof(v));
if (c == -1 && errno != EINTR) {
perror("Write to output file");
exit(EXIT_FAILURE);
}
Why it is checked if && errno != EINTR here ?
Looking for errno on man I found the following text about EINTR, but even if I visited man 7 signal that doesn't enlighten me.
EINTR Interrupted function call (POSIX.1); see signal(7).

Many system calls will report the EINTR error code if a signal occurred while the system call was in progress. No error actually occurred, it's just reported that way because the system isn't able to resume the system call automatically. This coding pattern simply retries the system call when this happens, to ignore the interrupt.
For instance, this might happen if the program makes use of alarm() to run some code asynchronously when a timer runs out. If the timeout occurs while the program is calling write(), we just want to retry the system call (aka read/write, etc).

the answers here are really good and i want to add some internal details :
System calls that are interrupted by signals can either abort and
return EINTR or automatically restart themselves
if and only if SA_RESTART is specified in sigaction(2)
and the one responsible for this task is the restart_block which used to track information and arguments for restarting system calls

From the man page on write:
The call was interrupted by a signal before any data was written

Related

SigHandler causing program to not terminate

Currently I am trying to create a signal handler that, when it receives a SIGTERM signal, it closes open network sockets and file descriptors.
Here is my SigHandler function
static void SigHandler(int signo){
if(signo == SIGTERM){
log_trace("SIGTERM received - handling signal");
CloseSockets();
log_trace("SIGTERM received - All sockets closed");
if (closeFile() == -1)
log_trace("SIGTERM received - No File associated with XXX open - continuing with shutdown");
else
log_trace("SIGTERM received - Closed File Descriptor for XXX - continuing with shutdown");
log_trace("Gracefully shutting down XXX Service");
} else {
log_trace("%d received - incompatible signal");
return;
}
exit(0);
}
This code below sits in main
if (sigemptyset(&set) == SIGEMPTYSET_ERROR){
log_error("Signal handling initialization failed");
}
else {
if(sigaddset(&set, SIGTERM) == SIGADDSET_ERROR) {
log_error("Signal SIGTERM not valid");
}
action.sa_flags = 0;
action.sa_mask = set;
action.sa_handler = &SigHandler;
if (sigaction(SIGTERM, &action, NULL) == SIGACTION_ERROR) {
log_error("SIGTERM handler initialization error");
}
}
When I send kill -15 PID, nothing happens. The process doesn't terminate, nor does it become a zombie process (not that it should anyway). I do see the traces printing within the SigHandler function however, so I know it is reaching that point in the code. It just seems that when it comes to exit(0), that doesn't work.
When I send SIGKILL (kill -9 PID) it kills the process just fine.
Apologies if this is vague, I'm still quite new to C and UNIX etc so I'm quite unfamiliar with most of how this works at a low level.
Your signal handler routine is conceptually wrong (it does not use just async-signal-safe functions). Read carefully signal(7) and signal-safety(7) to understand why. And your handler could apparently work most of the time but still be undefined behavior.
The usual trick is to set (in your signal handler) some volatile sig_atomic_t variable and test that variable outside of the signal handler.
Another possible trick is the pipe(7) to self trick (the Qt documentation explains it well), with your signal handler just doing a write(2) (which is async-signal-safe) to some global file descriptor obtained by e.g. pipe(2) (or perhaps the Linux specific eventfd(2)...) at program initialization before installing that signal handler.
A Linux specific way is to use signalfd(2) for SIGTERM and handle that in your own event loop (based upon poll(2)). That trick is conceptually a variant of the pipe to self one. But signalfd has some shortcomings, that a web search will find you easily.
Signals are conceptually hard to use (some view them as a design mistake in Unix), especially in multi-threaded programs.
You might want to read the old ALP book. It has some good explanations related to your issue.
PS. If your system is QNX you should read its documentation.
You should be using _exit from the signal handler instead, this also closes all the files.
Also read (very carefully) Basile's answer and take a long hard look at the list of async safe functions which you are allowed to use in signal handlers.
His advice about just changing a flag and testing it in your code is the best way if you need to do something you aren't allowed in the signal handler. Note that all blocking posix calls can be interrupted by signals so testing your atomic variable if you get an error on a blocking call (to say read) is a sure way to know if you have received a signal.

A signal interrupts the send method in blocking mode

I'm programming with socket which is in blocking mode, I have a question about send method.
In the man page of send method, it says:
[EINTR] A signal interrupts the system call before any data is transmitted.
It means if a signal interrupts the system call before any data is transmitted, the send would return -1 and errno would be set to EINTR.
My question is that if a part of data has been transmitted when a signal interrupts the system call, what will return. It seems that it shouldn't return -1 because it has send some data. I think it will return the number of data has been transmitted which means send method in the blocking mode may return less number of data than you passed as third parameter.
ssize_t send(int socket, const void *buffer, size_t length, int flags);
The other answers are pretty clear, but after reading some of your comments, I would like to add some further information.
First of all, you got the idea behind EINTR wrong. Getting interrupted by a signal in a syscall is not to be perceived as an error. The rationale behind EINTR in slow syscalls (slow syscalls are those that can block forever, like open(2) on some file types - terminal devices for example - accept(2), read(2) and write(2) on some devices - sockets included - etc.) is that if your program was blocked in a syscall and a signal was caught (while still blocked), then it is very likely (but not mandatory) that the signal handler changed state in your program and things are different, so the call returns prematurely with EINTR to give you a chance of doing anything that you might want to do. It is not an error like EINVAL or EBADF or other "real" errors - it's just the way the kernel tells you that a signal was caught.
If you don't want to do anything, then either set the SA_RESTART flag on the sa_flags field of struct sigaction when setting up the signal handler (which causes the syscall to be automatically restarted), or explicitly call send(2) again when it returns -1 with errno set to EINTR.
The bottom line is, there isn't an inherent limitation on the kernel that forces it to return to userspace when signals are caught. Instead, EINTR is just a convenient behavior that developers might find useful.
If the kernel was in the midst of transmitting data and a signal was raised, it's no big deal: if data is being transmitted, the syscall is doing progress. The kernel is in process context executing a syscall in behalf of the program that called it, so technically the process is not sleeping anymore. If a signal arrives, it will be pending until the kernel decides that it's time to deliver it to the process - most likely, this happens once send(2) returns.
The documentation is clear.
RETURN VALUE
On success, these calls return the number of bytes sent. On error, -1 is returned, and errno is set appropriately.
[...]
EINTR A signal occurred before any data was transmitted;
send() either returns
the number of bytes sent
or -1
If -1 is returned the reason is indicated via the value of errno.
If errno equals EINTR a signal interupted send() while no data had been received so far.
From this info above one can safely conclude that if data had been received the send() function would not return -1, not matter whether a signal was received or not.
[EINTR] A signal interrupts the system call before any data is transmitted.
This means that if send() start transmitting data, it won't be interrupted by any signals. So, transmission will block the receiving of signal until it finishes. The situation that send() may return less bytes of data than you passed as third parameter is usually due to the network problems, such as packets lost.

read from disk and EINTR

is it necessary to check for errno == EINTR if you read massive amounts of data? I use the pread() function to read. In all my time I have never seen EINTR returned, but I have seen some code online where it is explicitely checks for it.
so really is it necessary to check for EINTR and maybe repeat the call?
EINTR is returned when as system call is interrupted as a result of your process receiving a signal. If your process was blocked in the kernel, waiting for the read to complete, and a signal is caught, this may wake the kernel; this depends on if the operation is interruptable. The sleeping I/O routine is woken and is expected to return EINTR to user-space.
Just before the kernel returns to user space, it checks for pending signals. If a signal is pending, it will take the action associated with that signal. Possible actions include: dispatching the signal to a signal handler, killing your process, or ignoring the signal. Assuming this does not kill your process and/or your signal handler returns normally, the system call will return EINTR.
If you were not expecting this, you typically want to try the action again, but this can also be used as a way to gracefully abort an I/O operation. For example, alarm(2) can be used to implement a timeout, where SIGALRM is delivered if the I/O does not complete in a timely manner. In your signal handler, you could set a flag indicating a timeout and when your read operation returns EINTR, you can check for your timeout flag.
The reason is - on a busy system, for example, it is possible to have an interrupt on the read.
So, on your desktop you may never see it. On an overloaded server, you can.
Se Chapter 5 of Advanced Programming in the UNIX Environment - Stevens and Rago. There is a complete explanation.

Why C library functions should fail if any signal arises

I am going through the book "UNIX Systems Programming", and came across the below point.
It is good practice to check for EINTR error code whenever a C library functions are called (say close() ) because the library functions can fail if any signal is received by the process. If EINTR error has occurred, the corresponding C library call should be restarted.
while ((close(fd) == -1) && errno == EINTR); // close is restarted if it fails with EINTR error.
Question: Why the library function should fail if it gets a signal ?
When a signal is received, the corresponding handler is called. After the completion of the handler can't the library functions continue from the point it stopped ?
Why the library function should fail if it gets a Signal ?
Because that's how it's designed, and the goal is that if a signal arrives
while you are stuck in a blocking system call, the system call returns, and you have
a chance to act on the signal.
However, this has traditionally been implemented in many variants on different platforms.
After the completion of the handler can't the library functions continue from the point it stoped ?
Absolutely. If you want this behavior, you can set the SA_RESTART flag when you install a signal handler with sigaction().
Note, even with the SA_RESTART flag, there are still some system calls that are not automatically restarted. For Linux, you can see a list of which calls in the "Interruption of system calls and library functions by signal handlers" paragraph in the signal(7) man page . (If anyone knows a similar list defined by posix, I'd be grateful).
If you install a signal handler using signal() instead of sigaction(), it varies among unix variants whether system calls are automatically restarted or not. SySV derived platform typically does not restart system calls, while BSD dervied platform does.
while ((close(fd) == -1) && errno == EINTR); // close is restarted if it fails with EINTR error.
This is actually quite dangerouos. If close() fails with EINTR, the state of the file descriptor is unknown, meaning if the file descriptor really was closed, you risk a race condition that closes another unrelated file descriptor. This is considered a bug in the posix specification.

sigset: ignoring ctrl-c in Unix

I am trying to make my program ignore Ctrl+C in unix which seems to work, the issue is that it keep writing "Syntax error". Here is the code
extern "C" void ignore( int sig )
{
fprintf( stderr, "\n"); // Print a new line
// This function does nothing except ignore ctrl-c
}
int main()
{
// For ctrl-c
sigset( SIGINT, ignore );
while (1) {
getUserInput();
}
return 0;
}
Everytime I hit Ctrl+C it runs through getUserInput again, which is the expected behavior, but it writes "Syntax error" as well. I checked and the "ignore" function gets executed, and once it has been executed, then it prints the error message, I am not sure why.
Does anyone have any clues please?
Thank you very much,
Jary
Do not use sigset(). Although it is in POSIX 2008, it is marked obsolescent - and also unsafe in threaded programs.
You then have a choice between signal(), which is blessed by ISO C but has some undesirable characteristics, and sigaction() which is the preferred solution in POSIX systems.
One key point with signal handling is to ensure you do not trap any signals that are ignored when you enter the program - unless you know something that the caller can't (such as you need to trap SIGCHLD signals for dead children).
This leads to the standard formulations, preached since time immemorial, for signal():
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
signal(SIGINT, ignore);
Or, for sigaction():
struct sigaction new_sa;
struct sigaction old_sa;
sigfillset(&new_sa.sa_mask);
new_sa.sa_handler = SIG_IGN;
new_sa.sa_flags = 0;
if (sigaction(SIGINT, &new_sa, &old_sa) == 0 && old_sa.sa_handler != SIG_IGN)
{
new_sa.sa_handler = ignore;
sigaction(SIGINT, &new_sa, 0);
}
Since you do not show us the getUserInput() function, there is no way we can prognosticate on why you see 'Syntax Error'. However, if you have a grammar at work, it may well be that your read is not returning valid data, and the parser is unhappy with what is left in the buffer for it to process.
(Note: for more portability, you should probably use signal() (C standard library) or sigaction() (POSIX) instead of sigset(), but I don't think that's the problem here.)
You're not really ignoring the signal here; you're catching it and taking your own action - but it's likely that a system call has returned an error as a result of the interruption.
e.g. maybe you've caused a read system call to fail with EINTR. I suspect the real problem is that some code inside getUserInput() is not handling this error case.
You can ignore the signal completely by setting the handler to the special value SIG_IGN, which should work with any of sigset(), signal() or sigaction().
The "Syntax Error" message must be coming from the getUserInput function (or something it calls, of course) -- you could investigate by having that function print out what it is receiving and why it is complaining.
Note that the canonical way to ignore a signal is to use the pre-defined SIG_IGN as the signal handler. e.g.
sigset(SIGINT, SIG_IGN)
ignore() does a lot more than nothing.
When you run this code on UNIX, SIGINT is delivered asynchronously on the same thread that is executing getUserInput(). If a SIGINT arrives while getUserInput() is accessing stderr, the behavior is undefined because fprintf() usually isn't designed to be reentrant. (However, the behavior is likely to include garbled and/or repeated output. Did your program print "Syntax Error" before the SIGINT was delivered?) CERT and GNU libc have some more info about this.
If you were running this code on Win32, SIGINT would be delivered asynchronously in a separate thread.

Resources