blob: bb6c0e238ce78ac82faec45ca1a65063b96d6b48 [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/NetlinkServer.h"
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <linux/netlink.h>
#include <stdlib.h>
#include "NetlinkConnection.h"
#include <signal.h>
#define LOG_TAG "McDaemon"
#include "log.h"
//------------------------------------------------------------------------------
NetlinkServer::NetlinkServer(
ConnectionHandler * connectionHandler
):Server(connectionHandler, "dummy") {
}
//------------------------------------------------------------------------------
void NetlinkServer::run(
) {
do
{
LOG_I("%s: start listening on netlink bus", __func__);
// Open a socket
serverSock = socket(PF_NETLINK, SOCK_DGRAM, MC_DAEMON_NETLINK);
if (serverSock < 0)
{
LOG_E("run(): can't open socket, errno=%d", errno);
break;
}
// Fill in address structure and bind to socket
struct sockaddr_nl src_addr;
struct nlmsghdr *nlh = NULL;
struct iovec iov;
struct msghdr msg;
uint32_t len;
memset(&src_addr, 0, sizeof(src_addr));
src_addr.nl_family = AF_NETLINK;
src_addr.nl_pid = MC_DAEMON_PID; /* daemon pid */
src_addr.nl_groups = 0; /* not in mcast groups */
if(bind(serverSock, (struct sockaddr*)&src_addr, sizeof(src_addr)) < 0){
LOG_E("bind() to server socket failed, errno=%d(%s)",
errno, strerror(errno));
close(serverSock);
break;
}
// Start reading the socket
LOG_I("\n********* successfully initialized *********\n");
for (;;)
{
// This buffer will be taken over by the connection it was routed to
nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
memset(&msg, 0, sizeof(msg));
iov.iov_base = (void *)nlh;
iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_name = &src_addr;
msg.msg_namelen = sizeof(src_addr);
memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
// Read the incomming message and route it to the connection based
// on the incomming PID
if ((len = recvmsg(serverSock, &msg, 0)) < 0) {
LOG_E("%s: recvmsg() failed, errno=%d(%s)",
__func__, errno, strerror(errno));
break;
}
if (NLMSG_OK(nlh, len)) {
handleMessage(nlh);
} else {
break;
}
}
} while(false);
LOG_E("%s: exiting due to error, errno=%d(%s)",
__func__, errno, strerror(errno));
}
//------------------------------------------------------------------------------
void NetlinkServer::handleMessage(
struct nlmsghdr *nlh
) {
uint32_t seq = nlh->nlmsg_seq;
uint32_t pid = nlh->nlmsg_pid;
//LOG_I("%s: Handling NQ message for pid %u seq %u...", __func__, pid, seq);
uint64_t hash = hashConnection(pid, seq);
/* First cleanup the connection list */
cleanupConnections();
NetlinkConnection *connection = findConnection(hash);
// This is a message from a new client
if (connection == NULL) {
//LOG_I("%s: Cound't find the connection, creating a new one", __func__);
connection = new NetlinkConnection(this, serverSock, pid, seq);
// Add the new connection
insertConnection(hash, connection);
}
connection->handleMessage(nlh);
// Only handle connections which have not been detached
if(connection->detached == false)
{
if (!connectionHandler->handleConnection(connection))
{
LOG_I("%s: No command processed.", __func__);
connection->socketDescriptor = -1;
//Inform the driver
connectionHandler->dropConnection(connection);
// Remove connection from list
removeConnection(hash);
connection->socketDescriptor = -1;
delete connection;
}
// If connection data is set to NULL then device close has been called
// so we must remove all connections associated with this hash
else if (connection->connectionData == NULL &&
connection->detached == false)
{
delete connection;
}
}
}
//------------------------------------------------------------------------------
void NetlinkServer::detachConnection(
Connection *connection
) {
connection->detached = true;
}
//------------------------------------------------------------------------------
NetlinkServer::~NetlinkServer(
void
) {
connectionMap_t::iterator i;
// Shut down the server socket
close(serverSock);
// Destroy all client connections
for (i = peerConnections.begin(); i != peerConnections.end(); i++) {
if (i->second->detached == false) {
delete i->second;
}
}
peerConnections.clear();
}
//------------------------------------------------------------------------------
NetlinkConnection* NetlinkServer::findConnection(
uint64_t hash
) {
connectionMap_t::iterator i = peerConnections.find(hash);
if (i != peerConnections.end()) {
return i->second;
}
return NULL;
}
//------------------------------------------------------------------------------
void NetlinkServer::insertConnection(
uint64_t hash,
NetlinkConnection *connection
) {
peerConnections[hash] = connection;
}
/* This is called from multiple threads! */
//------------------------------------------------------------------------------
void NetlinkServer::removeConnection(
uint64_t hash
) {
connectionMap_t::iterator i = peerConnections.find(hash);
if (i != peerConnections.end()){
peerConnections.erase(i);
}
}
//------------------------------------------------------------------------------
void NetlinkServer::cleanupConnections(
void
) {
connectionMap_t::reverse_iterator i;
pid_t pid;
NetlinkConnection *connection = NULL;
// Destroy all client connections
for (i = peerConnections.rbegin(); i != peerConnections.rend(); ++i) {
connection = i->second;
// Only 16 bits are for the actual PID, the rest is session magic
pid = connection->peerPid & 0xFFFF;
//LOG_I("%s: checking PID %u", __func__, pid);
// Check if the peer pid is still alive
if (pid == 0) {
continue;
}
if (kill(pid, 0)) {
bool detached = connection->detached;
LOG_I("%s: PID %u has died, cleaning up session 0x%X",
__func__, pid, connection->peerPid);
connection->socketDescriptor = -1;
//Inform the driver
connectionHandler->dropConnection(connection);
// We aren't handling this connection anymore no matter what
removeConnection(connection->hash);
// Remove connection from list only if detached, the detached
// connections are managed by the device
if (detached == false) {
delete connection;
}
if (peerConnections.size() == 0) {
break;
}
i = peerConnections.rbegin();
}
}
}
/** @} */