blob: e890877376ef6744af8988db5a2a048c5e651bb2 [file] [log] [blame]
/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
* @{
* @file
*
* Connection server.
*
* Handles incoming socket connections from clients using the MobiCore driver.
*/
/*
* Copyright (c) 2013 TRUSTONIC LIMITED
* All rights reserved.
*
* 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. Neither the name of the TRUSTONIC LIMITED nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 COPYRIGHT HOLDER OR
* CONTRIBUTORS 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("NetlinkServer: Starting to listen on netlink bus");
// Open a socket
serverSock = socket(PF_NETLINK, SOCK_DGRAM, MC_DAEMON_NETLINK);
if (serverSock < 0) {
LOG_ERRNO("Opening socket");
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_ERRNO("Binding to server socket failed, because bind");
close(serverSock);
serverSock = -1;
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));
if (nlh == NULL) {
LOG_E("Allocation failure");
break;
}
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 incoming message and route it to the connection based
// on the incoming PID
if ((int) (len = recvmsg(serverSock, &msg, 0)) < 0) {
LOG_ERRNO("recvmsg");
break;
}
if (NLMSG_OK(nlh, len)) {
handleMessage(nlh);
} else {
break;
}
}
close(serverSock);
serverSock = -1;
} while (false);
LOG_W("Could not open netlink socket. KernelAPI disabled");
}
//------------------------------------------------------------------------------
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...", __FUNCTION__, 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", __FUNCTION__);
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.", __FUNCTION__);
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
if(serverSock != -1) {
close(serverSock);
serverSock = -1;
}
// 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", __FUNCTION__, 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",
__FUNCTION__, 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();
}
}
}
/** @} */