libt2n: (gerd) refactored connection classes
[libt2n] / src / socket_handler.cpp
diff --git a/src/socket_handler.cpp b/src/socket_handler.cpp
new file mode 100644 (file)
index 0000000..4fa27d0
--- /dev/null
@@ -0,0 +1,236 @@
+/***************************************************************************
+ *   Copyright (C) 2006 by Gerd v. Egidy                                   *
+ *   gve@intra2net.com                                                     *
+ *                                                                         *
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library 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 Lesser General Public License for more details.                   *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this program; if not, write to the                 *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <time.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sstream>
+
+#include "socket_handler.hxx"
+#include "t2n_exception.hxx"
+
+using namespace std;
+
+namespace libt2n
+{
+
+void socket_handler::set_socket_options(int sock)
+{
+    int i=1;
+
+    /* fast reuse enable */
+    if (setsockopt(sock,SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)) < 0)
+    {
+        string err="error setting socket option: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+
+    /* keepalive enable */
+    if (setsockopt(sock,SOL_SOCKET, SO_KEEPALIVE, &i, sizeof(i)) < 0)
+    {
+        string err="error setting socket option: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+
+    /* close on exec */
+    int fdflags;
+    fdflags=fcntl(sock,F_GETFD, 0);
+    if (fdflags < 0)
+    {
+        string err="fcntl error on socket: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+    fdflags |= FD_CLOEXEC;
+    if (fcntl(sock,F_SETFD,fdflags) < 0)
+    {
+        string err="fcntl error on socket: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+
+    /* non-blocking mode */
+    int flflags;
+    flflags=fcntl(sock,F_GETFL,0);
+    if (flflags < 0)
+    {
+        string err="fcntl error on socket: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+    flflags |= O_NONBLOCK;
+    if (fcntl(sock,F_SETFL,flflags) < 0)
+    {
+        string err="fcntl error on socket: ";
+        err+=strerror(errno);
+        log(error, err);
+        throw t2n_communication_error(err);
+    }
+}
+
+void socket_handler::close()
+{
+    ::close(sock);
+}
+
+bool socket_handler::data_waiting(long long usec_timeout)
+{
+    // just our socket
+    fd_set active_fd_set;
+    FD_ZERO (&active_fd_set);
+    FD_SET (sock, &active_fd_set);
+
+    /* set timeout */
+    struct timeval tval;
+    struct timeval *timeout_ptr;
+
+    if (usec_timeout == -1)
+        timeout_ptr = NULL;
+    else
+    {
+        timeout_ptr = &tval;
+
+        // timeout von long long usec in int sec + int usec umrechnen
+        tval.tv_sec = usec_timeout / 1000000;
+        tval.tv_usec = usec_timeout % 1000000;
+    }
+
+    if(select (FD_SETSIZE, &active_fd_set, NULL, NULL, timeout_ptr) > 0)
+        return true;
+    else
+        return false;
+}
+
+bool socket_handler::fill_buffer(std::string& buffer, long long usec_timeout)
+{
+    // fast path for timeout==-1
+    if (usec_timeout==-1 || data_waiting(usec_timeout))
+        return fill_buffer(buffer);
+    else
+        return false;
+}
+
+bool socket_handler::fill_buffer(std::string& buffer)
+{
+    bool try_again=false;
+
+    char socket_buffer[recv_buffer_size];
+
+    int nbytes = read (sock, socket_buffer, recv_buffer_size);
+    if (nbytes < 0)
+    {
+        if (errno == EAGAIN)
+            return false;                // no data was waiting
+        else if (errno == EINTR)
+        {
+            // interrupted, try again
+            try_again=true;
+        }
+        else
+        {
+            log(error,string("error reading from socket : ")+strerror(errno));
+            // TODO: exception?
+            return false;
+        }
+    }
+
+    // End-of-file
+    if (nbytes == 0 && !try_again)
+    {
+        close();
+        return false;
+    }
+
+    // Data read -> store it
+    if (nbytes > 0)
+        buffer.assign(socket_buffer,nbytes);
+
+    // more data waiting -> recurse
+    if (data_waiting())
+        fill_buffer(buffer);
+
+    if (nbytes > 0)
+        return true;
+    else
+        return false;
+}
+
+void socket_handler::write(const std::string& data)
+{
+    int offset = 0;
+    while (offset < data.size())
+    {
+        unsigned int write_size=write_block_size;
+
+        if (offset+write_size > data.size())
+            write_size = data.size()-offset;
+
+        int rtn;
+        while ((rtn=::write(sock, data.data()+offset, write_size)) &&
+               rtn == -1 && (errno == EAGAIN || errno == EINTR))
+        {
+            usleep (80000);
+            log(debug,"resuming write() call after EAGAIN or EINTR");
+        }
+
+        if (rtn == -1)
+        {
+            log(error,string("write() returned ")+strerror(errno));
+            // TODO: exception?
+            return;
+        }
+        else if (rtn != write_size)
+        {
+            ostringstream msg;
+            msg << "write() wrote " << rtn << " bytes, should have been " 
+                << write_size << " (complete: " << data.size() << ")";
+
+            log(error,msg.str());
+
+            // TODO: exception?
+            return;
+        }
+
+        offset += write_size;
+    }
+
+    return;
+}
+
+}