SIGIO on socket disconnect

Paul Vojta (vojta@math.berkeley.edu)
Thu, 6 May 1999 14:45:33 -0700


Folks:

Recently I have run across a problem with Linux 2.2 kernels concerning the
generation of SIGIO signals when the far end of a socket disconnects.
Under all other versions of Unix-ish operating systems available to me,
it is possible to receive a signal when the far end of a PF_UNIX socket
is closed, but not under Linux-2.2. I enclose a sample program below.

This problem came to light as Debian bug #28859 for xdvi. If one runs
xdvi with DISPLAY set to ":0", and then closes the xdvi window using the
window manager ``destroy'' function, then the xdvi process continues to
run. This is because xdvi expects to be woken up by a signal when that
happens, but no signal arrives. I have worked around this by resorting
to a less reliable way of combining poll() with signals, but I would
really rather not use this method. The problem also occurs when the
connection is closed due to server shutdown or server crash.

In addition, on several occasions I have seen questions posted to
comp.windows.x asking about how to interleave signal processing with
poll/select on the X socket, without using the toolkit method.
I have replied to them by suggesting that they look at the source to xdvi.
Therefore, this question is of more general interest than just for xdvi.

A sample program demonstrating this problem is as follows:

===============================================================================

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

int flag = 0;

static void
handler(int signo)
{
flag = 1;
}

main()
{
int sv[2];

if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv)) {
perror("socketpair");
exit(1);
}

signal(SIGIO, handler);

if (fcntl(sv[1], F_SETOWN, getpid()) == -1) {
perror("F_SETOWN");
exit(1);
}
if (fcntl(sv[1], F_SETFL, fcntl(sv[1], F_GETFL, 0) | FASYNC) == -1) {
perror("F_SETFL");
exit(1);
}

close(sv[0]);

sleep(3); /* give the signal a chance to occur */

puts(flag ? "SIGIO received" : "No signal received.");

return 0;
}

===============================================================================

I have run this program (or variants, as noted) on the following operating
systems, and in each case have been able to generate a SIGIO signal on
socket close:

O/S and version Notes
--------------- -----
SunOS 4.1.3 and 4.1.4
SunOS 5.5, 5.5.1, and 5.6 [1]
NeXT Mach 2.1
HP-UX B.10.10 [2]
SGI IRIX 6.2 and 6.3
Linux 2.0.36 Intel [3]

These are all Unix variants that I have access to (besides Linux-2.2).

Notes
=====
[1] For SunOS 5, it was necessary to replace the fcntl() calls with their
STREAMS equivalents, namely:

if (ioctl(sv[1], I_SETSIG, S_RDNORM | S_HANGUP | S_WRNORM) == -1) {
perror("ioctl");
exit(1);
}

[2] For HP-UX, the fcntl() calls were replaced by:

int arg;

arg = getpid();
if (ioctl(sv[1], SIOCSPGRP, &arg) == -1) {
perror("SIOCSPGRP");
exit(1);
}

arg = 1;
if (ioctl(sv[1], FIOASYNC, &arg) == -1) {
perror("FIOASYNC");
exit(1);
}

[3] I posted a shorter version of this note to
comp.os.linux.development.system, and received a (private) reply
from Andi Kleen. He disputed my assertion that this program worked
in Linux 2.0.36. I invite any neutral third party to comment on this
question, especially someone with access to Red Hat 5.2 (Apollo).

================

Ideally, I would prefer that all pollable events have the option of raising
a signal. That way, a program that needs to both reply to signals and
perform non-blocking I/O could do all of its blocking in sigpause().
It would then call poll() to check for I/O if it received the corresponding
signal. That is how xdvi works on most operating systems. Admittedly,
though, this (signals for all pollable events) may require extensive changes
to linux; I propose it only as a long-term goal. Having signals for
socket disconnects would be enough for xdvi.

Presently, under Linux 2.2, xdvi uses the fallback method of blocking
in poll(), and checking for EINTR upon return (and not using SIGIO at all).
This is less reliable because if a signal arrives at the same time as
the arguments to poll() are being pushed on the stack, then poll() will
still block until something else happens.

An extensive discussion of signal handling in X programs is available at
<URL:http://club.eng.cam.ac.uk/help/tpl/graphics/X/signals.html>.

Sincerely,

Paul Vojta
vojta@math.berkeley.edu

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/