/* * Copyright (c) 1995, 1996 Gunther Schadow. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "pg_config.h" IDENT("@(#) ansi-iopfx.c (Gunther Schadow) 11/16/96"); /* * Prefixes to be called before each i/o operation by the ANSI X3.28 * driver. Blocks until line is ready for the appropriate operation. * Timeout on expiration of timer D. */ #include "ansi.h" #include "socket.h" #include #include #include #include #include #include #include #ifdef _AIX #include #endif #ifdef hpux #include #endif /* _as_ipfx() -- input operation prefix * * select file for incoming data and exceptions. Blocks the process until * incoming data is ready for reading. Exceptional conditions (like * ioctl requests) are handled beforehand. * If the file is closed or becomes otherwiise unreadable, ipfx() * fails and sets errno to EPIPE should I also raise SIGPIPE? Or maybe * I should use ECONNRESET? * If timeout time expires a ipfx() fails and sets errno to ETIMERD. * The caller of _as_ipfx() must set the correct error according to * the timer it supplied. */ result _as_ipfx(ASTRM *asp, int timeout) { fd_set rdfds, ecfds; int nfds; do { #ifdef __GNUC__ struct timeval to = { timeout, 0 }; #else struct timeval to; to.tv_sec = timeout; to.tv_usec = 0; #endif FD_ZERO(&ecfds); FD_ZERO(&rdfds); FD_SET(asp->_fd, &rdfds); FD_SET(asp->_fd, &ecfds); select_again: nfds = select(asp->_fd+1, &rdfds, NULL, &ecfds, &to); if (nfds < 0) /* error */ { if (errno == EINTR) goto select_again; else { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":select: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } } else if (nfds == 0) /* timeout */ { syslog(LOG_WARNING, __FUNCTION__ ":select: timeout"); AS_ERROR(asp, AS_ETIMERD); return FAIL; } else if(FD_ISSET(asp->_fd, &ecfds)) { DBG(syslog(LOG_DEBUG, __FUNCTION__ ": exceptional condition")); if(_as_select_except_handler(asp) == FAIL) return FAIL; } } while(! FD_ISSET(asp->_fd, &rdfds)); return SUCCESS; } /* _as_opfx() -- output operation prefix * * selects the file descriptor for readyness to write and read data * and for exceptional conditions. If data is writeable, opfx() * succeeds. If data is readable, the data is simply thrown away. * There shouldn't be any incoming data, when it is time to write on * the ANSI line, should it? Exceptional conditions are also handled. * If the descriptor is closed, or becomes otherwhise unusable, a * SIGPIPE is raised and opfx() fails with errno set to EPIPE. */ result _as_opfx(ASTRM *asp, int timeout) { fd_set rdfds, wrfds, ecfds; int nfds; do { #ifdef __GNUC__ struct timeval to = { timeout, 0 }; #else struct timeval to; to.tv_sec = timeout; to.tv_usec = 0; #endif FD_ZERO(&ecfds); FD_ZERO(&rdfds); FD_ZERO(&wrfds); FD_SET(asp->_fd, &rdfds); FD_SET(asp->_fd, &wrfds); FD_SET(asp->_fd, &ecfds); select_again: nfds = select(asp->_fd+1, &rdfds, &wrfds, &ecfds, &to); if(nfds < 0) /* error */ { if (errno == EINTR) goto select_again; else { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":select: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } } if (nfds == 0) /* timeout */ { syslog(LOG_WARNING, __FUNCTION__ ":select: timer D timeout"); AS_ERROR(asp, AS_ETIMERD); return FAIL; } if(FD_ISSET(asp->_fd, &ecfds)) { DBG(syslog(LOG_DEBUG, __FUNCTION__ ": exceptional condition")); if(_as_select_except_handler(asp) == FAIL) return FAIL; } if(FD_ISSET(asp->_fd, &rdfds)) { char buf[AS_BUF_SIZE]; int cnt; #ifdef DEBUG switch(cnt = read(asp->_fd, buf, AS_BUF_SIZE -1)) { #else switch(cnt = read(asp->_fd, buf, AS_BUF_SIZE)) { #endif case FAIL: asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":read: %m"); AS_ERROR(asp, AS_EIO); return FAIL; case 0: syslog(LOG_WARNING, __FUNCTION__ ": file became unusable"); AS_ERROR(asp, AS_ECONNRESET); return FAIL; case 1: /* allow a (non standard) termination interrupt */ if(buf[0] == A_EOT) { syslog(LOG_WARNING, "non standard termination interrupt"); AS_ERROR(asp, AS_EINTR); return FAIL; } default: syslog(LOG_WARNING, __FUNCTION__ " junk on the line"); DBG(buf[cnt]=0); DBG(syslog(LOG_DEBUG, __FUNCTION__ " read: `%s'", buf)); } } } while(! FD_ISSET(asp->_fd, &wrfds)); return SUCCESS; } result _as_select_except_handler(ASTRM *asp) { #ifdef hpux register int fd = asp->_fd; struct request_info rqinf; /* Prevent a hang in the ioctl call */ attimeout(astimer_D /* seconds */) { AS_ERROR(asp, AS_ETIMERD); return FAIL; } if (ioctl(fd, TIOCREQCHECK, &rqinf) == FAIL) { if(errno == EINVAL) { syslog(LOG_WARNING, __FUNCTION__ ":TIOCREQCHECK: client died"); AS_ERROR(asp, AS_ECONNRESET); return FAIL; } else { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":TIOCREQCHECK: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } } else { int arg_buf[128]; if(rqinf.request == TIOCCLOSE) { DBG(syslog(LOG_DEBUG, __FUNCTION__ ": close request")); AS_ERROR(asp, AS_ECONNRESET); return FAIL; } else { DBG(syslog(LOG_DEBUG, __FUNCTION__ ": handle ioctl request:")); #ifdef __GNUC__ logioctl(LOG_INFO, __FUNCTION__, rqinf.request); #endif if(rqinf.argget != 0) { if(ioctl(fd, rqinf.argget, arg_buf) == FAIL) { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":TIOCARGGET: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } #ifdef __GNUC__ if(istermios(rqinf.request)) logtermios(LOG_DEBUG, __FUNCTION__, (struct termios *)arg_buf); #endif } tioctty(fd, 1); rqinf.return_value = ioctl(fd, rqinf.request, arg_buf); if(rqinf.return_value == FAIL) { rqinf.errno_error = errno; syslog(LOG_ERR, __FUNCTION__ ": ioctl failed: " "%d %m", rqinf.return_value); } else rqinf.errno_error = 0; tioctty(fd, 0); if(rqinf.argset != 0) { if(ioctl(fd, rqinf.argset, arg_buf) == FAIL) { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ": TIOCARGSET: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } if(istermios(rqinf.request)) logtermios(LOG_DEBUG, "", (struct termios *)arg_buf); } DBG(syslog(LOG_DEBUG, __FUNCTION__ ": ioctl request successful")); } if (ioctl(fd, TIOCREQSET, &rqinf) == FAIL) if(errno == EINVAL) { syslog(LOG_WARNING, __FUNCTION__ ":TIOCREQSET: client died"); AS_ERROR(asp, AS_ECONNRESET); return FAIL; } else { asp->_errno = errno; syslog(LOG_ERR, __FUNCTION__ ":TIOREQSET: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } } #endif return SUCCESS; }