/** @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(); | |
} | |
} | |
} | |
/** @} */ |