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

Seblu's avatar
Seblu committed
  SLS 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

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

#include "sls.hh"
#include "server.hh"
#include "error.hh"
#include "connection.hh"
#include "client.hh"

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

static const int BACKLOG = 10;
Seblu's avatar
Seblu committed

/*!
 * Constructor
 */
Server::Server() : max_conn_ (0), verbose_(false) {
  pthread_mutex_init(&threads_mutex_, 0);
}
Seblu's avatar
Seblu committed

/*!
 * Destructor
Seblu's avatar
Seblu committed
 */
Server::~Server() {
  pthread_mutex_destroy(&threads_mutex_);
Seblu's avatar
Seblu committed
}

/*!
 * Start server.
Seblu's avatar
Seblu committed
 */
void Server::start(int port, size_t max_conn, bool verbose) {
  if (max_conn <= 0)
    throw Error(ERR_SRV, "Too less max connection allowed");

  if (conn_.connected())
    throw Error(ERR_SRV, "Server already started");

  // Set options
  max_conn_ = max_conn;
  verbose_ = verbose;

  // listen on connection
  conn_.listen(port, BACKLOG);

  // treat new connections
  while (1) {

    // accept incoming connection
    Connection *nc = conn_.accept();

    // check max conn
    if (threads_.size() >= max_conn_) {
      std::cout << "Connection refused from ip " << nc->getremoteip()
		<< " on port " << nc->getremoteport()
		<< ": Max connections reached.\n";
      nc->disconnect();
      continue;
    }

    // Print connection
    std::cout << "New connection from ip " << nc->getremoteip()
	      << " on port " << nc->getremoteport();
    if (verbose_)
      std::cout << " (socket " << nc->getsocket() << ")" ;
    std::cout << ".\n";

    // Create new thread
    pthread_t t;
    pthread_attr_t t_attr;

    if (pthread_attr_init(&t_attr) != 0) {
      nc->disconnect();
      delete nc;
      throw Error(ERR_THREAD, (string) "Unable to init thread attr: " + strerror(errno));
    }

    // Set thread detachable. This mean it free ressource when its
    // execution is terminated. It's very important !
    if (pthread_attr_setdetachstate(&t_attr, PTHREAD_CREATE_DETACHED) != 0) {
      pthread_attr_destroy(&t_attr);
      nc->disconnect();
      delete nc;
      throw Error(ERR_THREAD, (string) "Unable to set thread attr: " + strerror(errno));
    }

    if (pthread_create(&t, &t_attr, start_client, nc) != 0) {
      pthread_attr_destroy(&t_attr);
      nc->disconnect();
      delete nc;
      throw Error(ERR_THREAD, (string) "Unable to create thread: " + strerror(errno));
    }

    pthread_attr_destroy(&t_attr);
  }
Seblu's avatar
Seblu committed
}

/*!
 * Stop server.
Seblu's avatar
Seblu committed
 */
void Server::stop() {
  pthread_mutex_lock(&threads_mutex_);

  // close all open thread
  for (Threadset::iterator it = threads_.begin(); it != threads_.end(); ++it)
    pthread_cancel((*it));
Seblu's avatar
Seblu committed

  // empty liste
  threads_.clear();

  pthread_mutex_unlock(&threads_mutex_);

  // close connexion
  conn_.disconnect();

  // reset max_conn_
  max_conn_ = 0;
Seblu's avatar
Seblu committed
}

/**
 * @return if server is started
Seblu's avatar
Seblu committed
 */
bool Server::started() {
  return conn_.connected();
}

/**
 * Entry point for new thread that represent client machine
 *
 * @param vfd pointer to the server socket
 *
 * @return error code
 */
void *Server::start_client(void *voidconn) {
  extern Server S;
  assert(S.started());

  const pthread_t t = pthread_self();


  Connection *conn = (Connection *) voidconn;
  assert(conn);

  // add into thread set
  pthread_mutex_lock(&S.threads_mutex_);
  S.threads_.insert(t);
  pthread_mutex_unlock(&S.threads_mutex_);

  try {
    Client *client = Client::login(*conn);

    if (client != 0) {
      client->exec();
      delete client;
    }
  }
  catch (const Error &e) {
    std::cerr << "On connection " << conn->getremoteip() << " port "
	      << conn->getremoteport() << ": " << e << ".\n";
  }

  // stop connexion
  conn->disconnect();

  // Print closing connection
  std::cout << "Disconnected from ip " << conn->getremoteip()
	    << " on port " << conn->getremoteport() << ".\n";

  // remove from thread set
  pthread_mutex_lock(&S.threads_mutex_);
  S.threads_.erase(t);
  pthread_mutex_unlock(&S.threads_mutex_);

  return 0;
Seblu's avatar
Seblu committed
}