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