/* * Copyright (c) 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. */ #pragma implementation #include "TTYSocket.h" #include #include #include #include #include #include #include #include #ifdef PROTOGEN # include "exception.h" # include "logfile.h" #else # include # include # define ERROR(fmt, args...) { fprintf(stderr, fmt "\n" , ## args); exit(1); } # define ERROS(fmt, args...) { fprintf(stderr, fmt ": " , ## args); \ perror(""); exit(1); } # define FATAL(fmt, args...) { fprintf(stderr, fmt "\n" , ## args); abort(); } # define WARNING(fmt, args...) { fprintf(stderr, fmt "\n" , ## args); } # define NOTICE(fmt, args...) { fprintf(stderr, fmt "\n" , ## args); } #endif /* * default termios settings */ speed_t TDspeed = B9600; // bps // 8 data, 1 stop, no parity, r/w, obey carrier, hard flow control tcflag_t TDcflag = CS8 | CREAD | CRTSCTS; tcflag_t TDiflag = IXON; // soft flow control for output tcflag_t TDoflag = 0; tcflag_t TDlflag = 0; cc_t TDvmin = 1; // just like the usual recv from a socket cc_t TDvtime = 0; static speed_t speed_to_bits(speed_t speed) { switch (speed) { case 0: return B0; case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; #ifdef B900 case 900: return B900; #endif case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; #ifdef B57600 case 57600: return B57600; #endif #ifdef B115200 case 115200: return B115200; #endif default: WARNING("speed %ld is not known to be allowed", (long)speed); return -1; }; } /********************************************************************** * argument parsing **********************************************************************/ /* Synopsis of the possible arguments for TTY: TTY(path, speed, data, stop, parity, control, iflag, oflag, cflag, lflag, cc...) Where the parameters stand for the following: path - the full path to the tty special file (e.g.: `/dev/ttyd01') speed - speed in bits/s data - number of data bits (5, 6, 7 or 8) stop - number of stop bits (1 or 2) parity - parity mode: (n = none, e = even, o = odd) control - flow control and how the carrier line is handled. Options are: h = hard flow control s = soft flow control n = no flow control c = ignore carrier line (clocal) u = hangup modem on last close (HUPCL) iflag, oflag, cflag, lflag - integer arguments (hex, oct, dec in the usual convention) that are assigned to the repective termios fields. Refer to termios(4) and /usr/include/termios.h for reference. cc - the control character set. The backslash `\' starts a number, which is interpreted as an ASCII value while anything is interpreted literally. Exceptions are the closing parenthesis which ends the parameter list and the asterisk `*' that always stands for the default value. Generally the asterisk can stand in place of any parameter and means that the default value be used. Likewhise, if an argument is left out `(a1,,a3)', the dafault is used for that argument as well. */ #include void TTYSocket::parse_args(const char *s, int l) { char args[l+1]; memcpy(args,s,l); args[l] = 0; // parse the argument string char *_argv[11]; char **argv = _argv; int argc = 0; { for(char *p = args; p != NULL; ) { char *val = strsep(&p, ","); *argv++ = val + strspn(val, " \t"); // delete spaces; argc++; // default strings are thus ""; } } argv = _argv; // set defaults speed_t speed = TDspeed; cfsetispeed(&_tio, speed); cfsetospeed(&_tio, speed); _tio.c_iflag = TDiflag; _tio.c_oflag = TDoflag; _tio.c_cflag = TDcflag; _tio.c_lflag = TDlflag; _tio.c_lflag = TDlflag; memset(_tio.c_cc, -1, NCCS); // '*' are xlated to -1; #define SETNUM(n,s) { char *e; long u=strtol(s,&e,0); if(e>s) n=u; } #define NEXTARG argv++; argc--; if(argc == 0) goto end; if(**argv!=0) // 1. path if(argc > 0) { _ttyname = strdup(*argv); for(char *p=_ttyname+strlen(_ttyname)-1; p>_ttyname; p--) if(! isspace(*p)) break; else *p = 0; } else ERROR("must specify at least a tty file"); NEXTARG // 2. speed { SETNUM(speed, *argv); (void) speed_to_bits(speed); // just does a test if(cfsetispeed(&_tio, speed) == -1) ERROS("cfsetispeed"); if(cfsetospeed(&_tio, speed) == -1) ERROS("cfsetspeed"); } NEXTARG // 3. data bits { for(char *p = *argv; *p != 0; p++) switch(*p) { case '5': _tio.c_cflag = _tio.c_cflag & ~CSIZE | CS5; break; case '6': _tio.c_cflag = _tio.c_cflag & ~CSIZE | CS6; break; case '7': _tio.c_cflag = _tio.c_cflag & ~CSIZE | CS7; break; case '8': _tio.c_cflag = _tio.c_cflag & ~CSIZE | CS8; break; case ' ': break; case '*': _tio.c_cflag = _tio.c_cflag & ~CSIZE | ~(TDcflag & CSIZE); break; default: ERROR("only 5, 6, 7, or 8 data bits allowed"); } } NEXTARG // 4. stop bits { for(char *p = *argv; *p != 0; p++) switch(*p) { case '1': _tio.c_cflag = _tio.c_cflag & ~CSTOPB; break; case '2': _tio.c_cflag = _tio.c_cflag | CSTOPB; break; case '*': _tio.c_cflag = TDcflag & CSTOPB; break; case ' ': break; default: ERROR("only 1 or 2 stop bits allowed"); } } NEXTARG // 5. parity { for(char *p = *argv; *p != 0; p++) switch(*p) { case 'n': _tio.c_cflag = _tio.c_cflag & ~( PARENB | PARODD ); break; case 'e': _tio.c_cflag = _tio.c_cflag | PARENB & ~PARODD; break; case 'o': _tio.c_cflag = _tio.c_cflag | PARENB | PARODD; break; case '*': _tio.c_cflag = _tio.c_cflag & ~(PARENB|PARODD) | (TDcflag & (PARENB|PARODD)); break; case ' ': break; default: ERROR("parity must be n, e or o"); } } NEXTARG // 6. { for(char *p = *argv; *p != 0; p++) switch(*p) { case 'h': _tio.c_cflag = _tio.c_cflag | CRTSCTS; break; case 's': _tio.c_iflag = _tio.c_iflag | IXON | IXOFF; break; case 'n': _tio.c_cflag &= ~CRTSCTS; _tio.c_iflag &= ~(IXON | IXOFF | IXANY); break; case 'c': _tio.c_cflag = _tio.c_cflag | CLOCAL; break; case 'u': _tio.c_cflag = _tio.c_cflag | HUPCL; break; case '*': _tio.c_cflag = _tio.c_cflag & ~( CRTSCTS | CLOCAL | HUPCL) | (TDcflag & ( CRTSCTS | CLOCAL | HUPCL)); _tio.c_iflag =_tio.c_iflag & ~( IXON | IXOFF | IXANY) | (TDcflag & ( IXON | IXOFF | IXANY)); break; case ' ': break; default: ERROR("control flags are: h, s, n, c, u"); } } NEXTARG // 7. c_iflag SETNUM(_tio.c_iflag, *argv); NEXTARG // 8. c_oflag SETNUM(_tio.c_oflag, *argv); NEXTARG // 9. c_cflag SETNUM(_tio.c_cflag, *argv); NEXTARG // 10. c_lflag SETNUM(_tio.c_lflag, *argv); NEXTARG // 11. c_cc { char *p; int i; for(i=0, p=*argv; i_fd = fd; ps->_state_plus = _state_plus & ~accept_able | connected | ( mode & send_recv ); ps->_ttyname = _ttyname; ps->_oflags = _oflags; ps->_tio = _tio; return ps; } size_t TTYSocket::send(const char *buf, size_t len, int msgf) { if(!(_state_plus & send_mode)) ERROR("illegal operation"); size_t n = write(_fd, buf, len); if(n == (size_t)-1) ERROS("write failed"); if( msgf & ( msg_eom | msg_eot ) ) { shutdown(send_mode); _state_plus |= eom_placed | eot_placed; } return n; } size_t TTYSocket::recv(char *buf, size_t max, int) { if(!(_state_plus & recv_mode)) ERROR("illegal operation"); size_t n = read(_fd, buf, max); if(n == (size_t)-1) ERROS("read failed"); if( n == 0 ) _state_plus |= eom_seen | eot_seen; return n; } void TTYSocket::shutdown(flags_t mode) { _state_plus = _state_plus & ~( mode & send_recv ); } void TTYSocket::close(flags_t) { if(_lock != NULL) last_close(); else ::close(_fd); _fd = -1; _state_plus = _state_plus & ~( connected | send_recv ); } bool TTYSocket::async(bool on, pid_t pid) { bool old = _state_plus & async_mode; int flags; if(fcntl(_fd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { if(pid != 0) if(fcntl(_fd, F_SETOWN, &pid) == -1) ERROS("fcntl:F_SETOWN:"); flags |= O_ASYNC; _state_plus |= async_mode; if(fcntl(_fd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); } else // off { flags &= ~O_ASYNC; if(fcntl(_fd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~async_mode; } return old; } bool TTYSocket::noblock(bool on) { bool old = _state_plus & noblock_mode; int flags; if(fcntl(_fd, F_GETFL, &flags) == -1) ERROS("fcntl:F_GETFL:"); if(on) { flags |= O_NONBLOCK; _state_plus |= noblock_mode; if(fcntl(_fd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETOWN:"); } else // off { flags &= ~O_NONBLOCK; if(fcntl(_fd, F_SETFL, &flags) == -1) ERROS("fcntl:F_SETFL:"); _state_plus &= ~noblock_mode; } return old; } bool TTYSocket::is_sendready(int sec, int usec) const { FdSet rfds; FdSet tfds(_fd, -1); select_again: int n = tfds.select(NULL, &rfds, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool TTYSocket::is_recvready(int sec, int usec) const { FdSet rfds; FdSet tfds(_fd, -1); select_again: int n = tfds.select(&rfds, NULL, NULL, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; } bool TTYSocket::is_exception(int sec, int usec) const { FdSet rfds; FdSet tfds(_fd, -1); select_again: int n = tfds.select(NULL, NULL, &rfds, sec, usec); if(n == -1) if(errno == EINTR) goto select_again; else ERROS("select"); return n != 0; }