/* * $Id: port.c,v 2.0.1.5 1996/06/26 18:39:38 alexis Exp alexis $ * * UPS Daemon * The Wild Wind Communications, 1995, 1996 * * See file LICENSE for the distribution terms of this software. */ #include "upsd.h" #include #include #include #include #include /* * Opens upsp->port.device for exclusive use and places the file * descriptor back to the structure. The open is non-blocking. * Initial terminal settings are stored in upsp->port.otty to be * restored in closeport(). This function also allocates space * for the port read queue. * * The functon returns -1 upon error or 0 if succeeded. */ int openport(void) { if((upsp->port.fd = open(upsp->port.device, O_RDWR | O_NONBLOCK)) == -1) { syslog(LOG_ERR, "openport: cannot open port %s: %m", upsp->port.device); return -1; } if(ioctl(upsp->port.fd, TIOCEXCL) == -1) { syslog(LOG_ERR, "openport: cannot lock port %s: %m", upsp->port.device); return -1; } if(tcgetattr(upsp->port.fd, &(upsp->port.otty)) == -1) { syslog(LOG_ERR, "openport: cannot get %s settings: %m", upsp->port.device); return -1; } if(tcsetattr(upsp->port.fd, TCSANOW, &(upsp->port.ntty)) == -1) { syslog(LOG_ERR, "openport: cannot set %s settings: %m", upsp->port.device); return -1; } if((upsp->port.queue.base = upsp->port.queue.head = upsp->port.queue.tail = xalloc(upsp->port.queue.size)) == NULL) { return -1; } if(ioctl(upsp->port.fd, TIOCSCTTY, 0) == -1) { syslog(LOG_ERR, "openport: cannot set ctty: %m"); return -1; } if(fcntl(upsp->port.fd, F_SETOWN, getpid()) == -1) { syslog(LOG_ERR, "openport: cannot set owner for %s: %m", upsp->port.device); return -1; } if(fcntl(upsp->port.fd, F_SETFL, O_NONBLOCK | O_ASYNC) == -1) { syslog(LOG_ERR, "openport: cannot enable async io: %m"); return -1; } return 0; } /* * Unlocks the port, restores its original terminal settings and * closes the port. * * Returns -1 upon error, or 0 if succeeded. */ int closeport(void) { if(fcntl(upsp->port.fd, F_SETFL, O_NONBLOCK) == -1) { syslog(LOG_ERR, "closeport: cannot disable async io: %m"); return -1; } if(tcsetattr(upsp->port.fd, TCSANOW, &(upsp->port.otty)) == -1) { syslog(LOG_ERR, "closeport: cannot set %s settings: %m", upsp->port.device); return -1; } if(ioctl(upsp->port.fd, TIOCNXCL) == -1) { syslog(LOG_ERR, "closeport: cannot unlock %s: %m", upsp->port.device); return -1; } if(close(upsp->port.fd) == -1) { syslog(LOG_ERR, "closeport: cannot close %s: %m", upsp->port.device); return -1; } if(upsp->port.queue.base != NULL) { free(upsp->port.queue.base); } return 0; } /* * Writes size characters to the specified port, from the buffer * pointed with data. * * Returns -1 upon error, or number of actually written characters * otherwise. */ int writeport(data, size) char *data; int size; { register int s; register int i; struct timeval delay = upsp->port.writedelay; for(i = 0, s = 0; i < size; select(0, NULL, NULL, NULL, &delay)) { s = write(upsp->port.fd, data + i, ((size - i) < upsp->port.writeblksz) ? size - i : upsp->port.writeblksz); if(s == -1) { syslog(LOG_ERR, "writeport: cannot write to %s: %m", upsp->port.device); return -1; } i += s; } return i; } /* * Reads size characters from the queue. * * Returns -1 upon error or number of actually read characters * otherwise. */ int readport(data, size, actual) char *data; int size; int actual; { register size_t s; if(size == 0) { return 0; } /* * The scheme of waiting for all the data to arrive is tight * tied with signal handling. The main idea is that the long * selecting will be interrupted when listenport() receives * SIGIO. Well, if the select() won't be interrupted then * we will sleep for port.timeout value independently of any I/O. */ if(actual) { while(queuelength() < size) { if(select(0, NULL, NULL, NULL, &upsp->port.timeout) == 0) { break; } else { if(errno != EINTR) { syslog(LOG_ERR, "readport: cannot sleep: %m"); return -1; } } } } if(upsp->port.queue.head <= upsp->port.queue.tail) { if(upsp->port.queue.tail - upsp->port.queue.head > size) { s = size; } else { s = upsp->port.queue.tail - upsp->port.queue.head; } bcopy(upsp->port.queue.head, data, s); if(actual) { upsp->port.queue.head += s; } return s; } else { s = upsp->port.queue.base + upsp->port.queue.size - upsp->port.queue.head; if(s > size) { s = size; bcopy(upsp->port.queue.head, data, s); if(actual) { upsp->port.queue.head += s; } return s; } else { /* the first bcopy */ bcopy(upsp->port.queue.head, data, s); data += s; /* the second bcopy */ if(upsp->port.queue.tail - upsp->port.queue.base > size - s) { bcopy(upsp->port.queue.base, data, size - s); if(actual) { upsp->port.queue.head = upsp->port.queue.base + size - s; } return size; } else { bcopy(upsp->port.queue.base, data, upsp->port.queue.tail - upsp->port.queue.base); if(actual) { upsp->port.queue.head = upsp->port.queue.tail; } return upsp->port.queue.tail - upsp->port.queue.base + s; } } /* NOTREACHED */ } /* NOTREACHED */ return 0; } /* * Listens and actually reads the port. Normally invoked as signal * handler caused by SIGIO. */ int listenport(void) { size_t s; void *t; switch((s = read(upsp->port.fd, upsp->port.queue.tail, upsp->port.queue.base + upsp->port.queue.size - upsp->port.queue.tail))) { case 0: return 0; /* NOTREACHED */ case -1: syslog(LOG_ERR, "listenport: cannot read %s: %m", upsp->port.device); return -1; /* NOTREACHED */ } t = upsp->port.queue.tail; if(upsp->port.queue.tail + s < upsp->port.queue.base + upsp->port.queue.size) { upsp->port.queue.tail += s; } else { upsp->port.queue.tail += s - upsp->port.queue.size; } if((t < upsp->port.queue.head && upsp->port.queue.tail >= upsp->port.queue.head) || (t > upsp->port.queue.head && upsp->port.queue.tail == upsp->port.queue.head)) { if(upsp->port.queue.tail + 1 < upsp->port.queue.base + upsp->port.queue.size) { upsp->port.queue.head = upsp->port.queue.tail + 1; } else { upsp->port.queue.head = upsp->port.queue.tail + 1 - upsp->port.queue.size; } syslog(LOG_WARNING, "listenport: overrun detected"); } return 0; } /* * Flushes the port cleansing the queue. */ void flushport(void) { upsp->port.queue.head = upsp->port.queue.tail = upsp->port.queue.base; return; } /* * Returns the effective length of the queue. */ size_t queuelength(void) { return (upsp->port.queue.head <= upsp->port.queue.tail) ? upsp->port.queue.tail - upsp->port.queue.head : (upsp->port.queue.base + upsp->port.queue.size - upsp->port.queue.head) + (upsp->port.queue.tail - upsp->port.queue.base); }