I'm trying to write a program that receives I/O on many sockets. I want the
program to be notified by the kernel via SIGIO and I want the kernel to also
to tell the program which descriptor the I/O condition has changed on.
Solaris 2.7 man pages say the __data.__file.__fd field is not available for
SIGPOLL (aka SIGIO on Solaris). Linux 2.2.14-5.0 seems to send back an fd of
0 all the time.
Is there a way this can be done? To get an idea of what I'm trying to do see
code below.
Any help will be greatly appreciated,
-sm
ps> Please cc: shekharm...@yahoo.com if you can.
sample code: sigio.c (Solaris 2.7 version, found on net and modified, thanks
Andrew Gierth)
----------------------------------------------------------------------------
--------------------
/* SIGIO and SIGURG example. (C) 1997 Andrew Gierth. */
/* Tested on FreeBSD. Please report any changes required for other */
/* systems. #ifdefs are speculative, but should work. */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/filio.h>
#include <sys/sockio.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#define TCP_PORT 8888
/* This example uses SIGIO and SIGURG on a TCP socket for convenience. */
/* In practice, SIGIO is almost useless on stream sockets, because it */
/* gets generated by far too many different events. */
/* To test, just telnet to port 8888. Use telnet's 'synch' command to */
/* generate OOB data for SIGURG. */
/* The program simply echos data in hex, bracketing all 'urgent' data */
/* in []. */
/* I say again: USE SIGIO ON STREAM SOCKETS ONLY AS A LAST RESORT. */
/* handle cruft */
#if EWOULDBLOCK == EAGAIN
# define IS_WOULDBLOCK(err) ((err)==EAGAIN)
#else
# define IS_WOULDBLOCK(err) (((err)==EAGAIN)||((err)==EWOULDBLOCK))
#endif
/* useful functions */
void errexit(const char *msg)
{
perror(msg);
exit(1);
}
/* this is done this way in an attempt to be safe in a signal handler. */
void complain(const char *msg,...)
{
va_list va;
char buf[1024];
va_start(va,msg);
vsprintf(buf, msg, va);
va_end(va);
write(STDERR_FILENO, buf, strlen(buf));
}
/* might need fcntl(F_SETFL), or ioctl(FIONBIO) */
/* Posix.1g says fcntl */
/* NEVER, EVER USE O_NDELAY HERE */
#ifdef O_NONBLOCK
int set_nonblock(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1)
return -1;
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}
#else
int set_nonblock(int fd)
{
int yes = 1;
return ioctl(fd, FIONBIO, &yes);
}
#endif
/* might need fcntl(F_SETOWN), or ioctl(FIOSETOWN), or ioctl(SIOCSPGRP) */
/* Posix.1g says fcntl */
#ifdef F_SETOWN
int set_owner(int fd)
{
return fcntl(fd, F_SETOWN, getpid());
}
#else
int set_owner(int fd)
{
pid_t pid = getpid();
return ioctl(fd, FIOSETOWN, &pid);
}
#endif
/* might need fcntl(F_SETFL), or ioctl(FIOASYNC) */
/* Posix.1g says fcntl */
/* DONT use I_SETSIG unless portability isn't an issue - remember, */
/* SOCKETS ARE NOT STREAMS! */
#ifdef O_ASYNC
int set_async(int fd)
{
int flags = fcntl(fd, F_GETFL, 0);
fprintf(stderr,"Using O_ASYNC\n");
if (flags == -1)
return -1;
return fcntl(fd, F_SETFL, flags | O_ASYNC);
}
#else
int set_async(int fd)
{
int yes = 1;
fprintf(stderr,"Using FIOASYNC\n");
return ioctl(fd, FIOASYNC, &yes);
}
#endif
int sockatmark(int sock)
{
int flag = 0;
if (ioctl(sock, SIOCATMARK, &flag) < 0)
return -1;
return (flag != 0) ? 1 : 0;
}
/* Ought to be able to do this with SIOCATMARK, but on my FreeBSD system */
/* it wasn't behaving in any sort of consistent fashion. */
int urgent_pending(int sock)
{
struct timeval tv;
fd_set fds;
FD_ZERO(&fds);
FD_SET(sock,&fds);
tv.tv_sec = 0;
tv.tv_usec = 0;
return select(sock+1,NULL,NULL,&fds,&tv);
}
/*-------------------------------------------------------------------------*
/
int sock;
sigset_t sigio_set;
sigset_t sigurg_set;
sigset_t empty_set;
volatile sig_atomic_t urgmode = 0;
volatile sig_atomic_t sigiocount = 0;
volatile sig_atomic_t sigurgcount = 0;
void sigio_handler(int sig, siginfo_t* si, void* v)
{
char inbuf[64];
char outbuf[4];
int i,rc,flag;
sigset_t sigs;
sigiocount++;
if (si) {
if (si->si_code == SI_NOINFO)
fprintf(stderr, "no info returned in siginfo :(\n");
else
fprintf(stderr, "received SIGIO with fd=%d, signo=%d\n",
si->__data.__file.__fd, si->si_signo);
}
else fprintf(stderr, "siginfo is null\n");
for (;;)
{
rc = read(sock, inbuf, sizeof(inbuf));
if (rc < 0 && IS_WOULDBLOCK(errno))
return;
if (rc < 0)
{
complain("error on read: %d (%s)\n", errno, strerror(errno));
_exit(1);
}
if (rc == 0)
{
complain("Done.\n");
complain("Total SIGIOs : %ld\n", (long) sigiocount);
complain("Total SIGURGs: %ld\n", (long) sigurgcount);
_exit(0);
}
for (i = 0; i < rc; i++)
{
outbuf[0] = ' ';
outbuf[1] = "0123456789ABCDEF"[(inbuf[i] >> 4) & 0x0F];
outbuf[2] = "0123456789ABCDEF"[inbuf[i] & 0x0F];
write(sock, outbuf, 3);
}
sigprocmask(SIG_BLOCK, &sigurg_set, &sigs);
if (urgmode && !urgent_pending(sock))
{
write(sock, "]", 1);
urgmode = 0;
}
sigprocmask(SIG_SETMASK, &sigs, NULL);
}
}
void sigurg_handler(int sig)
{
sigurgcount++;
urgmode = 1;
write(sock, " [", 2);
}
main()
{
struct sigaction sa;
struct sockaddr_in addr;
int addrlen = sizeof(addr);
int fd = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
if (fd < 0)
errexit("socket");
fprintf(stderr, "listen socket created, fd = %d\n", fd);
sigemptyset(&empty_set);
sigemptyset(&sigio_set);
sigaddset(&sigio_set,SIGIO);
sigemptyset(&sigurg_set);
sigaddset(&sigurg_set,SIGURG);
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) < 0)
errexit("setsockopt");
/* set this in the listen socket; it will be inherited. */
if (setsockopt(fd, SOL_SOCKET, SO_OOBINLINE, &yes, sizeof(int)) < 0)
errexit("setsockopt");
addr.sin_family = AF_INET;
addr.sin_port = htons(TCP_PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *) &addr, addrlen) < 0)
errexit("bind");
if (listen(fd,5) < 0)
errexit("listen");
sock = accept(fd, (struct sockaddr *) &addr, &addrlen);
fprintf(stderr, "accept socket created, fd = %d\n", sock);
if (sock < 0)
errexit("accept");
close(fd);
/* block SIGIO and SIGURG while we set everything up. */
sigprocmask(SIG_BLOCK, &sigio_set, NULL);
sigprocmask(SIG_BLOCK, &sigurg_set, NULL);
if (set_nonblock(sock) < 0)
errexit("set_nonblock");
if (set_owner(sock) < 0)
errexit("set_owner");
if (set_async(sock) < 0)
errexit("set_async");
sa.sa_handler = sigio_handler;
sa.sa_flags = SA_SIGINFO;
sigemptyset(&sa.sa_mask);
sigaction(SIGIO, &sa, NULL);
sa.sa_handler = sigurg_handler;
sa.sa_flags = SA_SIGINFO;
sa.sa_mask = sigio_set;
sigaction(SIGURG, &sa, NULL);
/* data may have arrived while we were setting up, so generate */
/* SIGIO now. */
raise(SIGIO);
/* unblock everything, then pause. */
sigprocmask(SIG_SETMASK, &empty_set, NULL);
for(;;)
pause();
/*NOTREACHED*/
return 0;
}
/* end */
>Hi:
>
>I'm trying to write a program that receives I/O on many sockets. I want the
>program to be notified by the kernel via SIGIO and I want the kernel to also
>to tell the program which descriptor the I/O condition has changed on.
>
>Solaris 2.7 man pages say the __data.__file.__fd field is not available for
>SIGPOLL (aka SIGIO on Solaris). Linux 2.2.14-5.0 seems to send back an fd of
>0 all the time.
Much of siginfo_t argument to signal handlers is not filled in in
Linux 2.2. Things work correctly in 2.4.
Cheers
Michael