Skip to content
connection.cc 4.22 KiB
Newer Older
Seblu's avatar
Seblu committed
/*
  This file is part of SLC.
Seblu's avatar
Seblu committed
  Copyright (C) 2008 Sebastien LUTTRINGER <contact@seblu.net>
Seblu's avatar
Seblu committed

  SLC is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
Seblu's avatar
Seblu committed
  the Free Software Foundation; version 2 of the License.
Seblu's avatar
Seblu committed

  SLC 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 SLC; if not, write to the Free Software
Seblu's avatar
Seblu committed
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Seblu's avatar
Seblu committed
*/

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
#include <poll.h>
#include "slc.hh"
#include "options.hh"
#include "connection.hh"
#include "screen.hh"
#include "error.hh"

Seblu's avatar
Seblu committed
pthread_mutex_t  mtx_socket = PTHREAD_MUTEX_INITIALIZER;

Connection::Connection() : socket_(0) {}
Seblu's avatar
Seblu committed
bool Connection::connected() const {
Seblu's avatar
Seblu committed
  return socket_ > 0;
Seblu's avatar
Seblu committed
}

void Connection::start() {
Seblu's avatar
Seblu committed
  if (connected())
    return;

  // make connection
  connect_(O.server.c_str(), O.port);

  // send login info

  // send pass info

Seblu's avatar
Seblu committed
  // Start reader thread
  pthread_create(&g_thread_reader, 0, thread_reader, 0);
}

void Connection::stop() {
Seblu's avatar
Seblu committed
  if (!connected())
    return;
Seblu's avatar
Seblu committed
  disconnect_();

void Connection::connect_(const char *addr, int port) {
  struct sockaddr_in daddr;
  struct hostent *h;

  // Check no existing connexion
  assert(socket_ == 0);

  // retrieve remote host info
  h = gethostbyname(addr);
Seblu's avatar
Seblu committed
  if (h == 0) {
    S << "Unable to resolve: " << addr  << ": " << hstrerror(h_errno) << ".\n";
    return;
  }

  // create socket
Seblu's avatar
Seblu committed
  pthread_mutex_lock(&mtx_socket);
  socket_ = socket(PF_INET, SOCK_STREAM, 0);
Seblu's avatar
Seblu committed
  if (socket_ == -1) {
    S << "Unable to create socket: " << strerror(errno) << ".\n";
    socket_ = 0;
Seblu's avatar
Seblu committed
    pthread_mutex_unlock(&mtx_socket);
Seblu's avatar
Seblu committed
    return;
  }
Seblu's avatar
Seblu committed
  pthread_mutex_unlock(&mtx_socket);

  daddr.sin_family = AF_INET;
  daddr.sin_port = htons(port);
  daddr.sin_addr = *((struct in_addr *) h->h_addr);
  memset(daddr.sin_zero, '\0', sizeof daddr.sin_zero);

  S << "Connecting to "  << h->h_name  << " (" << inet_ntoa(*(struct in_addr*)h->h_addr)
    << ") on port " << port << "...\n";

  // connect
Seblu's avatar
Seblu committed
  pthread_mutex_lock(&mtx_socket);
  int con = connect(socket_, (struct sockaddr *) &daddr, sizeof daddr);
  pthread_mutex_unlock(&mtx_socket);
  if (con == -1) {
    disconnect_();
Seblu's avatar
Seblu committed
    S << "Unable to connect: " << strerror(errno) << ".\n";
Seblu's avatar
Seblu committed
    return;
  }

  S << "Connected to " << h->h_name << " (" << inet_ntoa(*(struct in_addr*)h->h_addr)
Seblu's avatar
Seblu committed
    << ") on port " << port << ".\n";
}

void Connection::disconnect_() {
Seblu's avatar
Seblu committed
  assert(socket_ > 0);
Seblu's avatar
Seblu committed
  pthread_mutex_lock(&mtx_socket);
  close(socket_);
  socket_ = 0;
Seblu's avatar
Seblu committed
  pthread_mutex_unlock(&mtx_socket);
Seblu's avatar
Seblu committed


void Connection::send(const string &data) {
  send(data.c_str(), data.length());
}

void Connection::send(const char* buf, size_t len) {
Seblu's avatar
Seblu committed
  assert(socket_ > 0);
  pthread_mutex_lock(&mtx_socket);
Seblu's avatar
Seblu committed
  write(socket_, buf, len);
Seblu's avatar
Seblu committed
  pthread_mutex_unlock(&mtx_socket);
Seblu's avatar
Seblu committed
}

void Connection::sendln(const string &data) {
  sendln(data.c_str(), data.length());
}

void Connection::sendln(const char* buf, size_t len) {
Seblu's avatar
Seblu committed
  assert(socket_ > 0);

  pthread_mutex_lock(&mtx_socket);
Seblu's avatar
Seblu committed
  write(socket_, buf, len);
  write(socket_, "\n", 1);
Seblu's avatar
Seblu committed
  pthread_mutex_unlock(&mtx_socket);
Seblu's avatar
Seblu committed
}
Seblu's avatar
Seblu committed

string Connection::recvln() {
Seblu's avatar
Seblu committed
  assert(socket > 0);

  do {
    // check EOL char
    size_t offset = rbuf_.find('\n');
    if (offset != string::npos) {
      string s = rbuf_.substr(0, offset);
      rbuf_.erase(0, offset + 1);
      return s;
    }

    // wait incoming data
    static struct pollfd spfd[1] = {{socket_, POLLIN|POLLPRI|POLLHUP|POLLERR, 0}};
    poll(spfd, 1, -1);

    // lock before read
    pthread_mutex_lock(&mtx_socket);

    // read data
    static char local_buf[MAX_LINE_SIZE];
    int ret = ::recv(socket_, local_buf, MAX_LINE_SIZE, MSG_DONTWAIT);

    // unlock
    pthread_mutex_unlock(&mtx_socket);

    if (ret == -1) {
      disconnect_();
      *local_buf = 0;
    }
    else local_buf[ret] = 0;

    // add read data in buffer
    rbuf_ += local_buf;

  } while (1);
  assert(1);
Seblu's avatar
Seblu committed
}