1/** @addtogroup MCD_MCDIMPL_DAEMON_SRV
2 * @{
3 * @file
4 *
5 * Connection server.
6 *
7 * Handles incoming socket connections from clients using the MobiCore driver.
8 *
9 * <!-- Copyright Giesecke & Devrient GmbH 2009 - 2012 -->
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote
20 *    products derived from this software without specific prior
21 *    written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
24 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
29 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35#include "public/NetlinkServer.h"
36#include <unistd.h>
37#include <string.h>
38#include <errno.h>
39#include <linux/netlink.h>
40
41#include <stdlib.h>
42#include "NetlinkConnection.h"
43#include <signal.h>
44
45#define LOG_TAG "McDaemon"
46#include "log.h"
47
48//------------------------------------------------------------------------------
49NetlinkServer::NetlinkServer(
50    ConnectionHandler *connectionHandler
51): Server(connectionHandler, "dummy")
52{
53}
54
55
56//------------------------------------------------------------------------------
57void NetlinkServer::run(
58)
59{
60    do {
61        LOG_I("NetlinkServer: Starting to listen on netlink bus");
62
63        // Open a socket
64        serverSock = socket(PF_NETLINK, SOCK_DGRAM,  MC_DAEMON_NETLINK);
65        if (serverSock < 0) {
66            LOG_ERRNO("Opening socket");
67            break;
68        }
69
70        // Fill in address structure and bind to socket
71        struct sockaddr_nl src_addr;
72        struct nlmsghdr *nlh = NULL;
73        struct iovec iov;
74        struct msghdr msg;
75        uint32_t len;
76
77        memset(&src_addr, 0, sizeof(src_addr));
78        src_addr.nl_family = AF_NETLINK;
79        src_addr.nl_pid = MC_DAEMON_PID;  /* daemon pid */
80        src_addr.nl_groups = 0;  /* not in mcast groups */
81        if (bind(serverSock, (struct sockaddr *)&src_addr, sizeof(src_addr)) < 0) {
82            LOG_ERRNO("Binding to server socket failed, because bind");
83            close(serverSock);
84            break;
85        }
86
87        // Start reading the socket
88        LOG_I("\n********* successfully initialized *********\n");
89
90        for (;;) {
91            // This buffer will be taken over by the connection it was routed to
92            nlh = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
93            memset(&msg, 0, sizeof(msg));
94            iov.iov_base = (void *)nlh;
95            iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
96            msg.msg_iov = &iov;
97            msg.msg_iovlen = 1;
98            msg.msg_name = &src_addr;
99            msg.msg_namelen = sizeof(src_addr);
100
101            memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
102
103            // Read the incomming message and route it to the connection based
104            // on the incomming PID
105            if ((len = recvmsg(serverSock, &msg, 0)) < 0) {
106                LOG_ERRNO("recvmsg");
107                break;
108            }
109
110            if (NLMSG_OK(nlh, len)) {
111                handleMessage(nlh);
112            } else {
113                break;
114            }
115        }
116    } while (false);
117
118    LOG_ERRNO("Exiting NetlinkServer! Because it");
119}
120
121//------------------------------------------------------------------------------
122void NetlinkServer::handleMessage(
123    struct nlmsghdr *nlh
124)
125{
126    uint32_t seq = nlh->nlmsg_seq;
127    uint32_t pid = nlh->nlmsg_pid;
128    //LOG_I("%s: Handling NQ message for pid %u seq %u...", __FUNCTION__, pid, seq);
129    uint64_t hash = hashConnection(pid, seq);
130    /* First cleanup the connection list */
131    cleanupConnections();
132
133    NetlinkConnection *connection = findConnection(hash);
134    // This is a message from a new client
135    if (connection == NULL) {
136        //LOG_I("%s: Cound't find the connection, creating a new one", __FUNCTION__);
137        connection = new NetlinkConnection(this, serverSock, pid, seq);
138        // Add the new connection
139        insertConnection(hash, connection);
140    }
141
142    connection->handleMessage(nlh);
143
144    // Only handle connections which have not been detached
145    if (connection->detached == false) {
146        if (!connectionHandler->handleConnection(connection)) {
147            LOG_I("%s: No command processed.", __FUNCTION__);
148            connection->socketDescriptor = -1;
149            //Inform the driver
150            connectionHandler->dropConnection(connection);
151
152            // Remove connection from list
153            removeConnection(hash);
154            connection->socketDescriptor = -1;
155            delete connection;
156        }
157        // If connection data is set to NULL then device close has been called
158        // so we must remove all connections associated with this hash
159        else if (connection->connectionData == NULL &&
160                 connection->detached == false) {
161            delete connection;
162        }
163    }
164}
165
166
167//------------------------------------------------------------------------------
168void NetlinkServer::detachConnection(
169    Connection *connection
170)
171{
172    connection->detached = true;
173}
174
175
176//------------------------------------------------------------------------------
177NetlinkServer::~NetlinkServer(
178    void
179)
180{
181    connectionMap_t::iterator i;
182    // Shut down the server socket
183    close(serverSock);
184
185    // Destroy all client connections
186    for (i = peerConnections.begin(); i != peerConnections.end(); i++) {
187        if (i->second->detached == false) {
188            delete i->second;
189        }
190    }
191    peerConnections.clear();
192}
193
194
195//------------------------------------------------------------------------------
196NetlinkConnection *NetlinkServer::findConnection(
197    uint64_t hash
198)
199{
200    connectionMap_t::iterator i = peerConnections.find(hash);
201    if (i != peerConnections.end()) {
202        return i->second;
203    }
204
205    return NULL;
206}
207
208
209//------------------------------------------------------------------------------
210void NetlinkServer::insertConnection(
211    uint64_t hash,
212    NetlinkConnection *connection
213)
214{
215    peerConnections[hash] = connection;
216}
217
218/* This is called from multiple threads! */
219//------------------------------------------------------------------------------
220void NetlinkServer::removeConnection(
221    uint64_t hash
222)
223{
224    connectionMap_t::iterator i = peerConnections.find(hash);
225    if (i != peerConnections.end()) {
226        peerConnections.erase(i);
227    }
228}
229
230//------------------------------------------------------------------------------
231void NetlinkServer::cleanupConnections(
232    void
233)
234{
235    connectionMap_t::reverse_iterator i;
236    pid_t pid;
237    NetlinkConnection *connection = NULL;
238    // Destroy all client connections
239    for (i = peerConnections.rbegin(); i != peerConnections.rend(); ++i) {
240        connection = i->second;
241        // Only 16 bits are for the actual PID, the rest is session magic
242        pid = connection->peerPid & 0xFFFF;
243        //LOG_I("%s: checking PID %u", __FUNCTION__, pid);
244        // Check if the peer pid is still alive
245        if (pid == 0) {
246            continue;
247        }
248        if (kill(pid, 0)) {
249            bool detached = connection->detached;
250            LOG_I("%s: PID %u has died, cleaning up session 0x%X",
251                  __FUNCTION__, pid, connection->peerPid);
252
253            connection->socketDescriptor = -1;
254            //Inform the driver
255            connectionHandler->dropConnection(connection);
256
257            // We aren't handling this connection anymore no matter what
258            removeConnection(connection->hash);
259
260            // Remove connection from list only if detached, the detached
261            // connections are managed by the device
262            if (detached == false) {
263                delete connection;
264            }
265            if (peerConnections.size() == 0) {
266                break;
267            }
268            i = peerConnections.rbegin();
269        }
270    }
271}
272
273/** @} */
274