http://www.erlenstar.demon.co.uk/usenet/sigio.txt Newsgroups: comp.unix.programmer Subject: SIGIO and SIGURG example From: Andrew Gierth Organization: disorganised X-Attribution: AG X-Mayan-Date: Long count = 12.19.3.16.16; tzolkin = 5 Cib; haab = 19 Pax X-Newsreader: Gnus v5.3/Emacs 19.34 Date: 21 Feb 1997 02:58:38 +0000 Message-ID: <873euqn9up.fsf@erlenstar.demon.co.uk> Well, somebody said "so", so here it is. I added SIGURG handling to it as well, just for fun. Open question: why couldn't I reliably detect the OOB mark using the sockatmark() function? Andrew. --8<-------------------------------------------------------------------- /* 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 #include #include #include #include #include #include #include #include #include #include #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); if (flags == -1) return -1; return fcntl(fd, F_SETFL, flags | O_ASYNC); } #else int set_async(int fd) { int yes = 1; 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) { char inbuf[64]; char outbuf[4]; int i,rc,flag; sigset_t sigs; sigiocount++; 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"); 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); 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 = 0; sigemptyset(&sa.sa_mask); sigaction(SIGIO, &sa, NULL); sa.sa_handler = sigurg_handler; sa.sa_flags = 0; 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 */ 1G