/* -*- Mode: c++; -*- */ /* -------------------------------------------------------------------- * Filename: * src/io/io.h * * Description: * Implementation of the IO class. * * Authors: * Andreas Aardal Hanssen * * Bugs: * * ChangeLog: * * -------------------------------------------------------------------- * Copyright 2002-2005 Andreas Aardal Hanssen * * 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., 59 Temple Street #330, Boston, MA 02111-1307, USA. * -------------------------------------------------------------------- */ #ifdef HAVE_CONFIG_H #include #endif #include #include "session.h" #include "io.h" using namespace ::std; using namespace Binc; //------------------------------------------------------------------------ IO::IO() { pid = getpid(); enabled = true; flushesOnEndl = true; mode = MODE_PLAIN; useLogPrefix = false; seqnr = 0; buffersize = 8192; transfertimeout = 0; inputsize = 0; inputlimit = true; } //------------------------------------------------------------------------ IO::IO(FILE *fp) { pid = getpid(); setFd(fp); enabled = true; flushesOnEndl = true; mode = MODE_PLAIN; useLogPrefix = false; seqnr = 0; buffersize = 8192; transfertimeout = 0; inputsize = 0; inputlimit = true; } //------------------------------------------------------------------------ IO::~IO(void) { switch (mode) { case MODE_SYSLOG: closelog(); break; default: break; } } //------------------------------------------------------------------------ void IO::enable(void) { enabled = true; } //------------------------------------------------------------------------ void IO::enableLogPrefix(void) { useLogPrefix = true; } //------------------------------------------------------------------------ void IO::disable(void) { enabled = false; } //------------------------------------------------------------------------ void IO::flushContent(void) { if (!enabled) return; string s = outputBuffer.str(); string tmpstr; if (mode == MODE_SYSLOG) { tmpstr = ""; for (string::iterator i = s.begin(); i != s.end(); ++i) if (*i != '\r' && *i != '\n') { tmpstr += *i; } else { writeStr(tmpstr); tmpstr = ""; } if (tmpstr != "") writeStr(tmpstr); } else writeStr(s); outputBuffer.clear(); } //------------------------------------------------------------------------ void IO::flushOnEndl(void) { flushesOnEndl = true; } //------------------------------------------------------------------------ void IO::noFlushOnEndl(void) { flushesOnEndl = false; } //------------------------------------------------------------------------ void IO::clear(void) { if (!enabled) return; outputBuffer.clear(); } //------------------------------------------------------------------------ void IO::setModeSyslog(const string &servicename, int facility = LOG_DAEMON) { static string sname; sname = servicename; if (mode != MODE_SYSLOG) { openlog(sname.c_str(), LOG_PID, facility); mode = MODE_SYSLOG; } } //------------------------------------------------------------------------ void IO::setFd(FILE *fp) { fpout = fp; } //------------------------------------------------------------------------ void IO::resetInput(void) { inputsize = 0; } //------------------------------------------------------------------------ void IO::enableInputLimit(void) { inputlimit = true; } //------------------------------------------------------------------------ void IO::disableInputLimit(void) { inputlimit = false; } //------------------------------------------------------------------------ IO &IO::operator << (std::ostream &(*man)(std::ostream &)) { if (!enabled) return *this; if (useLogPrefix && outputBuffer.getSize() == 0) { outputBuffer << pid; outputBuffer << " "; outputBuffer << seqnr++; if (logprefix != "") outputBuffer << " [" << logprefix << "]"; outputBuffer << " "; } static std::ostream &(*endl_funcpt)(std::ostream&) = std::endl; if (man == endl_funcpt) { outputBuffer << "\r\n"; if (flushesOnEndl) flushContent(); } else outputBuffer << man; if ((int) outputBuffer.getSize() >= buffersize) flushContent(); return *this; } //------------------------------------------------------------------------ int IO::select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, int &timeout) { if (timeout == -1) return 1; struct timeval t1; struct timeval t2; int res; if (gettimeofday(&t1, 0) == -1) { setLastError("Failure in select: gettimeofday failed."); return -1; } do { struct timeval t; t.tv_sec = timeout; t.tv_usec = 0; res = ::select(maxfd, readfds, writefds, exceptfds, timeout ? &t : 0); if (gettimeofday(&t2, 0) == -1) { setLastError("Failure in select: gettimeofday failed."); return -1; } timeout = 1000*(t2.tv_sec - t1.tv_sec) + (t2.tv_usec - t1.tv_usec)/1000; if (timeout < 0) timeout = 0; if (res == 0) return 0; if (res > 0) return res; } while (res == -1 && errno == EINTR); return -1; } //------------------------------------------------------------------------ void IO::writeStr(const string s) { if (s == "") return; if (mode == MODE_PLAIN) { alarm(transfertimeout); int n = fwrite(s.c_str(), 1, s.length(), fpout); fflush(fpout); alarm(0); if (n != (int) s.length()) { // ignore error return; } Session::getInstance().addWriteBytes(n); } else if (mode == MODE_SYSLOG) { alarm(transfertimeout); syslog(LOG_INFO, "%s", s.c_str()); alarm(0); } else { // ignore error return; } } //------------------------------------------------------------------------ int IO::readChar(int timeout, bool retry) { string s; int ret = readStr(s, 1, timeout, retry); if (ret == 1) return s[0]; return ret; } //------------------------------------------------------------------------ int IO::fillBuffer(int timeout, bool retry) { // Fill the input buffer with data fd_set rfds; FD_ZERO(&rfds); FD_SET(0, &rfds); int r = select(fileno(stdin) + 1, &rfds, 0, 0, timeout); if (r == 0) { setLastError("client timed out"); return -2; } else if (r == -1) { setLastError("error reading from client"); return -1; } char buf[1024]; int readBytes = read(fileno(stdin), buf, sizeof(buf)); if (readBytes <= 0) { setLastError("client disconnected"); return -1; } Session::getInstance().addReadBytes(readBytes); for (int i = 0; i < readBytes; ++i) inputBuffer.push_front(buf[i]); return readBytes; } //------------------------------------------------------------------------ int IO::readStr(string &data, int bytes, int timeout, bool retry) { data = ""; bool second = false; for (;;) { // First, empty data from the input buffer while (inputBuffer.size() > 0) { if (bytes != -1 && data.length() == (unsigned int) bytes) break; data += inputBuffer.back(); inputBuffer.pop_back(); } // If bytes == -1, this means we want to fill the buffer once // more, then empty this over in data, then finally return. if (bytes == -1 && second) return data.length(); // If we got as much as we wanted, return. if (bytes != -1 && data.length() == (unsigned int) bytes) return bytes; // If we reached the input limit, abort. if (inputlimit && data.length() >= 8192) { setLastError("input limit is 8192 characters."); return -1; } // Fill the buffer with data, int ret = fillBuffer(timeout, retry); if (ret < 0) return ret; second = true; } return data.length(); } //------------------------------------------------------------------------ void IO::unReadChar(int c_in) { inputBuffer.push_back(c_in); } //------------------------------------------------------------------------ void IO::unReadChar(const string &s_in) { if (s_in.length() != 0) { int c = s_in.length() - 1; while (c >= 0) { unReadChar(s_in[c]); --c; } } } //------------------------------------------------------------------------ int IO::pending(void) const { return 0; } //------------------------------------------------------------------------ const string &IO::getLastError(void) const { return lastError; } //------------------------------------------------------------------------ void IO::setLastError(const string &e) { lastError = e; } //------------------------------------------------------------------------ IOFactory::IOFactory(void) { } //------------------------------------------------------------------------ IOFactory::~IOFactory(void) { } //------------------------------------------------------------------------ IOFactory &IOFactory::getInstance(void) { static IOFactory iofactory; return iofactory; } //------------------------------------------------------------------------ void IOFactory::assign(int id, IO *io) { ioers[id] = io; } //------------------------------------------------------------------------ IO &IOFactory::get(int id) { if (ioers.find(id) == ioers.end()) { static IO NIL; return NIL; } return *ioers[id]; }