/* * 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-ctrl.c (Gunther Schadow) 10/13/96"); #include "ansi.h" #include #include #include #include #include #include #ifdef _AIX #include #endif result _as_send_ctrl(ASTRM *asp, const char *prefix, int ctrl, char BccBlk) { u_int i, idx; u_int nccc = _A_NCCC(ctrl); int fd = asp->_fd; bool has_blk = _A_BLKP(ctrl); bool has_bcc = _A_BCCP(ctrl); #ifdef __GNUC__ char buf[AS_PFX_SIZE + nccc + (has_blk ? 1 : 0) + (has_bcc ? 1 : 0)]; #else char buf[AS_PFX_SIZE + 4 + 2]; #endif if(ctrl == -1) return SUCCESS; buf[0] = 0; if(prefix != NULL && *prefix != 0 && ctrl != A_EOT && ctrl != A_DEOT) strncat(buf, prefix, A_PREFIX_MAXLEN); idx = strlen(buf); for(i = 0; i < nccc; i++) buf[idx++] = _A_DICC(ctrl,i); if(has_bcc) buf[idx++] = BccBlk; if(has_blk) buf[idx++] = (BccBlk & 7) | 0x30; if(_as_opfx(asp, astimer_D) == FAIL) return FAIL; if(write(fd, buf, idx) == FAIL) { asp->_errno = errno; syslog(LOG_ERR, "_as_send_ctrl: write: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } return SUCCESS; } /* * Receive a complete control compound. With optional prefix and following * block check character (BCC) or link block number (BLK) if appropriate. * The prefix is written into the buffer pointed to by `prefix', the block * check character or link block number is stored into where `BccBlk' is * pointing to. `BccBlk' and `prefix' can be given as NULL if the respective * information is not needed. If neither BCC nor BLK is not appropriate * FAIL is stored instead. * If the control compound read was a mandatory disconnect (DEOT), then * FAIL is returned with AS_ECONNRESET. */ int _as_recv_ctrl(ASTRM *asp, char *prefix, char *BccBlk, int timeout) { char pfxbuf[AS_PFX_SIZE]; char *pfx, c; int ctrl, i, nccs, fd = asp->_fd; if(prefix == NULL) pfx = pfxbuf; else pfx = prefix; i = 0; do { if(_as_ipfx(asp, timeout) == FAIL) return FAIL; if(read(fd, &c, 1) < 1) goto read_fail; if(i > A_PREFIX_MAXLEN) { pfx[i] = 0; syslog(LOG_ERR, __FUNCTION__ ":control sequence expected at `%s'", pfx); AS_ERROR(asp, AS_ESYNTAX); return FAIL; } pfx[i++] = c; } while(!iscntrl(c)); pfx[--i] = 0; /* c is a Control Character */ ctrl = _A_ASCC(0,0,c); nccs = _A_NCCS(c); for(i = 1; i < nccs; i++) { if(_as_ipfx(asp, astimer_D) == FAIL) return FAIL; if(read(fd, &c, 1) < 1) goto read_fail; ctrl = _A_ASCC(ctrl,i,c); } if(BccBlk != NULL) if(_A_BCCP(ctrl) || _A_BLKP(ctrl)) { if(_as_ipfx(asp, astimer_D) == FAIL) return FAIL; if(read(fd, &c, 1) < 1) goto read_fail; DBG(syslog(LOG_DEBUG, "BccBlk = `%c' %d", c, (int)c)); if(_A_BLKP(ctrl)) *BccBlk = c & 7; else *BccBlk = c; } else *BccBlk = FAIL; /* * Mandatory Disconnect is mandatory :-) */ if(ctrl == A_DEOT) { AS_ERROR(asp, AS_ECONNRESET); return FAIL; } return ctrl; read_fail: asp->_errno = errno; syslog(LOG_ERR, "_as_recv_ctrl: read: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } result _as_send_ack(ASTRM *asp, const char *prefix, int ack) { asp->_resp = ack; return _as_send_ctrl(asp, prefix, ack, 0); } /* _as_recv_ack * * Receive an acknowledgement of any kind and compare it with the ack * parameter. Return 0 (= SUCCESS) if ack matches, A_ACK if ack does * not match, A_NAK if NAK was received and FAIL if an error occured. * If EOT was read instead of ACK or NAK, FAIL with AS_EINTR. If there * is an ENQ received return A_ENQ. */ int _as_recv_ack(ASTRM *asp, char *prefix, int ack) { int ctrl; char pfx_buf[AS_PFX_SIZE]; char *pfx; if(prefix == NULL) pfx = pfx_buf; else pfx = prefix; if((ctrl = _as_recv_ctrl(asp, pfx, NULL, astimer_A)) == FAIL) { if(AS_ERRNO(asp) == AS_ETIMERD) AS_ERROR(asp, AS_ETIMERA); return FAIL; } if(ctrl == ack) return SUCCESS; else if(ctrl == A_NAK) { syslog(LOG_WARNING, "_as_recv_ack: received `%s'-NAK", pfx); return A_NAK; } else if(ctrl == A_EOT) { AS_ERROR(asp, AS_EINTR); return FAIL; } else if(ctrl == A_ENQ) { syslog(LOG_WARNING, "_as_recv_ack: received `%s'-ENQ", pfx); return A_ENQ; } else { char c0 = _A_DICC(ctrl, 0); char c1 = _A_DICC(ctrl, 1); if(c0 == A_DLE) { if(_A_DICC(ack,0) == A_DLE) if(strchr("01234567", c1) != NULL) { syslog(LOG_WARNING, "_as_recv_ack: " "wrong `%s'-ACKN(%c) must be %c", pfx, c1, _A_DICC(ack,1)); return A_ACK; } syslog(LOG_ERR, "_as_recv_ack: " "unexpected ctrl sequence `%s'-DLE-%c", pfx, c1); AS_ERROR(asp, AS_ESYNTAX); return FAIL; } else { syslog(LOG_ERR, "_as_recv_ack: " "unexpected ctrl sequence `%s'-0x%04x", pfx, ctrl); AS_ERROR(asp, AS_ESYNTAX); return FAIL; } } } /* Receive ENQ: call a blocking read(), if there is an ENQ found, try * nonblocking read() until there are no more ENQ characters pending. * If the last character was an ENQ return SUCCESS. If the last * character was an EOT the remote side already gave up: return FAIL * with AS_EABORT. If the last character is neither ENQ nor EOT nor DEOT * return FAIL with AS_ESYNTAX. * * Instead of calling multiple single read()s, get a whole block and * test it for ENQ/EOT in memory. */ #define ENQBUFSIZE 1204 result _as_recv_enq(ASTRM *asp) { register int fd = asp->_fd; char buf[ENQBUFSIZE]; register char *p = buf; register int i, cnt; read_again: if(_as_ipfx(asp, astimer_E) == FAIL) { if(AS_ERRNO(asp) == AS_ETIMERD) AS_ERROR(asp, AS_ETIMERE); return FAIL; } cnt = read(fd, buf, ENQBUFSIZE); if(cnt == FAIL) { asp->_errno = errno; syslog(LOG_ERR, "_as_recv_enq:read: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } p=buf; for(i = 0; i < cnt && *p == A_ENQ; i++) p++; if(i == cnt) /* only ENQs */ { if(cnt < ENQBUFSIZE) /* that's all */ { DBG(syslog(DEBUG, __FUNCTION__ ": success")); return SUCCESS; } else { /* A whole block full of ENQs has been read. We have to poll * for more to be read. */ fd_set fds; struct timeval to = { 0, 0 }; int n; FD_ZERO(&fds); FD_SET(fd, &fds); n = select(fd + 1, &fds, NULL, NULL, &to); if(n == 0) /* nothing left to be read */ return SUCCESS; else if(n == 1) goto read_again; else { asp->_errno = errno; syslog(LOG_ERR, "_as_recv_enq:select: %m"); AS_ERROR(asp, AS_EIO); return FAIL; } } } else { if(*p == A_EOT || (*p == A_DLE && *(char*)(p+1) == A_EOT)) { /* we are too late */ AS_ERROR(asp, AS_EABORT); return FAIL; } else { syslog(LOG_ERR, "_as_recv_enq: " "unexpected character follows ENQ 0x02x `%c'", (int)*p, *p); AS_ERROR(asp, AS_ESYNTAX); return FAIL; } } }