blob: 085d0fd36e892ee1ecdc2dd298b950177cdeee93 [file] [log] [blame]
/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
* @{
* @file
*
* Connection server.
*
* Handles incoming socket connections from clients using the MobiCore driver.
*
* <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "public/Server.h"
#include <unistd.h>
#include <string.h>
#include <errno.h>
//#define LOG_VERBOSE
#include "log.h"
//------------------------------------------------------------------------------
Server::Server(
ConnectionHandler *connectionHandler,
const char *localAddr
) : socketAddr(localAddr)
{
this->connectionHandler = connectionHandler;
}
//------------------------------------------------------------------------------
void Server::run(
void
)
{
do {
LOG_I("Server: start listening on socket %s", socketAddr.c_str());
// Open a socket (a UNIX domain stream socket)
serverSock = socket(AF_UNIX, SOCK_STREAM, 0);
if (serverSock < 0) {
LOG_ERRNO("Can't open stream socket, because socket");
break;
}
// Fill in address structure and bind to socket
struct sockaddr_un serverAddr;
serverAddr.sun_family = AF_UNIX;
strncpy(serverAddr.sun_path, socketAddr.c_str(), sizeof(serverAddr.sun_path) - 1);
uint32_t len = strlen(serverAddr.sun_path) + sizeof(serverAddr.sun_family);
// Make the socket in the Abstract Domain(no path but everyone can connect)
serverAddr.sun_path[0] = 0;
if (bind(serverSock, (struct sockaddr *) &serverAddr, len) < 0) {
LOG_ERRNO("Binding to server socket failed, because bind");
}
// Start listening on the socket
if (listen(serverSock, LISTEN_QUEUE_LEN) < 0) {
LOG_ERRNO("listen");
break;
}
LOG_I("\n********* successfully initialized Daemon *********\n");
for (;;) {
fd_set fdReadSockets;
// Clear FD for select()
FD_ZERO(&fdReadSockets);
// Select server socket descriptor
FD_SET(serverSock, &fdReadSockets);
int maxSocketDescriptor = serverSock;
// Select socket descriptor of all connections
for (connectionIterator_t iterator = peerConnections.begin();
iterator != peerConnections.end();
++iterator) {
Connection *connection = (*iterator);
int peerSocket = connection->socketDescriptor;
FD_SET(peerSocket, &fdReadSockets);
if (peerSocket > maxSocketDescriptor) {
maxSocketDescriptor = peerSocket;
}
}
// Wait for activities, select() returns the number of sockets
// which require processing
LOG_V(" Server: waiting on sockets");
int numSockets = select(
maxSocketDescriptor + 1,
&fdReadSockets,
NULL, NULL, NULL);
// Check if select failed
if (numSockets < 0) {
LOG_ERRNO("select");
break;
}
// actually, this should not happen.
if (0 == numSockets) {
LOG_W(" Server: select() returned 0, spurious event?.");
continue;
}
LOG_V(" Server: events on %d socket(s).", numSockets);
// Check if a new client connected to the server socket
if (FD_ISSET(serverSock, &fdReadSockets)) {
do {
LOG_V(" Server: new connection attempt.");
numSockets--;
struct sockaddr_un clientAddr;
socklen_t clientSockLen = sizeof(clientAddr);
int clientSock = accept(
serverSock,
(struct sockaddr *) &clientAddr,
&clientSockLen);
if (clientSock <= 0) {
LOG_ERRNO("accept");
break;
}
Connection *connection = new Connection(clientSock, &clientAddr);
peerConnections.push_back(connection);
LOG_I(" Server: new socket connection established and start listening.");
} while (false);
// we can ignore any errors from accepting a new connection.
// If this fail, the client has to deal with it, we are done
// and nothing has changed.
}
// Handle traffic on existing client connections
connectionIterator_t iterator = peerConnections.begin();
while ( (iterator != peerConnections.end())
&& (numSockets > 0) ) {
Connection *connection = (*iterator);
int peerSocket = connection->socketDescriptor;
if (!FD_ISSET(peerSocket, &fdReadSockets)) {
++iterator;
continue;
}
numSockets--;
// the connection will be terminated if command processing
// fails
if (!connectionHandler->handleConnection(connection)) {
LOG_I(" Server: dropping connection.");
//Inform the driver
connectionHandler->dropConnection(connection);
// Remove connection from list
delete connection;
iterator = peerConnections.erase(iterator);
continue;
}
++iterator;
}
}
} while (false);
LOG_ERRNO("Exiting Server, because");
}
//------------------------------------------------------------------------------
void Server::detachConnection(
Connection *connection
)
{
LOG_V(" Stopping to listen on notification socket.");
for (connectionIterator_t iterator = peerConnections.begin();
iterator != peerConnections.end();
++iterator) {
Connection *tmpConnection = (*iterator);
if (tmpConnection == connection) {
peerConnections.erase(iterator);
LOG_I(" Stopped listening on notification socket.");
break;
}
}
}
//------------------------------------------------------------------------------
Server::~Server(
void
)
{
// Shut down the server socket
close(serverSock);
// Destroy all client connections
connectionIterator_t iterator = peerConnections.begin();
while (iterator != peerConnections.end()) {
Connection *tmpConnection = (*iterator);
delete tmpConnection;
iterator = peerConnections.erase(iterator);
}
}
/** @} */