--- /dev/null
+/***************************************************************************
+ * 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;
+}
+
+}