diff options
Diffstat (limited to 'utils/hwstub/lib/hwstub_net.cpp')
-rw-r--r-- | utils/hwstub/lib/hwstub_net.cpp | 1351 |
1 files changed, 1351 insertions, 0 deletions
diff --git a/utils/hwstub/lib/hwstub_net.cpp b/utils/hwstub/lib/hwstub_net.cpp new file mode 100644 index 0000000000..f561a1004b --- /dev/null +++ b/utils/hwstub/lib/hwstub_net.cpp | |||
@@ -0,0 +1,1351 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2016 by Amaury Pouly | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | #include "hwstub_net.hpp" | ||
22 | #include <sys/socket.h> | ||
23 | #include <sys/un.h> | ||
24 | #include <unistd.h> | ||
25 | #include <cstddef> | ||
26 | #include <arpa/inet.h> | ||
27 | #include <sys/types.h> | ||
28 | #include <netdb.h> | ||
29 | |||
30 | namespace hwstub { | ||
31 | namespace net { | ||
32 | |||
33 | /** | ||
34 | * Context | ||
35 | */ | ||
36 | context::context() | ||
37 | :m_state(state::HELLO), m_error(error::SUCCESS) | ||
38 | { | ||
39 | } | ||
40 | |||
41 | context::~context() | ||
42 | { | ||
43 | } | ||
44 | |||
45 | std::shared_ptr<context> context::create_socket(int socket_fd) | ||
46 | { | ||
47 | // NOTE: can't use make_shared() because of the protected ctor */ | ||
48 | return std::shared_ptr<socket_context>(new socket_context(socket_fd)); | ||
49 | } | ||
50 | |||
51 | std::string context::default_unix_path() | ||
52 | { | ||
53 | return "hwstub"; | ||
54 | } | ||
55 | |||
56 | std::string context::default_tcp_domain() | ||
57 | { | ||
58 | return "localhost"; | ||
59 | } | ||
60 | |||
61 | std::string context::default_tcp_port() | ||
62 | { | ||
63 | return "6666"; | ||
64 | } | ||
65 | |||
66 | namespace | ||
67 | { | ||
68 | /* len is the total length, including a 0 character if any */ | ||
69 | int create_unix_low(bool abstract, const char *path, size_t len, bool conn, | ||
70 | std::string *error) | ||
71 | { | ||
72 | struct sockaddr_un address; | ||
73 | if(len > sizeof(address.sun_path)) | ||
74 | { | ||
75 | if(error) | ||
76 | *error = "unix path is too long"; | ||
77 | return -1; | ||
78 | } | ||
79 | int socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); | ||
80 | if(socket_fd < 0) | ||
81 | { | ||
82 | if(error) | ||
83 | *error = "socket() failed"; | ||
84 | return -1; | ||
85 | } | ||
86 | memset(&address, 0, sizeof(struct sockaddr_un)); | ||
87 | address.sun_family = AF_UNIX; | ||
88 | /* NOTE memcpy, we don't want to add a extra 0 at the end */ | ||
89 | memcpy(address.sun_path, path, len); | ||
90 | /* for abstract name, replace first character by 0 */ | ||
91 | if(abstract) | ||
92 | address.sun_path[0] = 0; | ||
93 | /* NOTE sun_path is the last field of the structure */ | ||
94 | size_t sz = offsetof(struct sockaddr_un, sun_path) + len; | ||
95 | /* NOTE don't give sizeof(address) because for abstract names it would contain | ||
96 | * extra garbage */ | ||
97 | if(conn) | ||
98 | { | ||
99 | if(connect(socket_fd, (struct sockaddr *)&address, sz) != 0) | ||
100 | { | ||
101 | close(socket_fd); | ||
102 | if(error) | ||
103 | *error = "connect() failed"; | ||
104 | return -1; | ||
105 | } | ||
106 | else | ||
107 | return socket_fd; | ||
108 | } | ||
109 | else | ||
110 | { | ||
111 | if(bind(socket_fd, (struct sockaddr *)&address, sz) != 0) | ||
112 | { | ||
113 | close(socket_fd); | ||
114 | if(error) | ||
115 | *error = "bind() failed"; | ||
116 | return -1; | ||
117 | } | ||
118 | else | ||
119 | return socket_fd; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | int create_tcp_low(const std::string& domain, const std::string& _port, bool server, std::string *error) | ||
124 | { | ||
125 | std::string port = _port.size() != 0 ? _port : context::default_tcp_port(); | ||
126 | int socket_fd = -1; | ||
127 | struct addrinfo hints; | ||
128 | memset(&hints, 0, sizeof(struct addrinfo)); | ||
129 | hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */ | ||
130 | hints.ai_socktype = SOCK_STREAM; | ||
131 | hints.ai_flags = 0; | ||
132 | hints.ai_protocol = 0; /* any protocol */ | ||
133 | |||
134 | struct addrinfo *result; | ||
135 | int err = getaddrinfo(domain.c_str(), port.c_str(), &hints, &result); | ||
136 | if(err != 0) | ||
137 | { | ||
138 | if(error) | ||
139 | *error = std::string("getaddrinfo failed: ") + gai_strerror(err); | ||
140 | return -1; | ||
141 | } | ||
142 | |||
143 | /* getaddrinfo() returns a list of address structures. | ||
144 | * Try each address until we successfully connect(2). | ||
145 | * If socket(2) (or connect(2)/bind(2)) fails, we (close the socket | ||
146 | * and) try the next address. */ | ||
147 | for(struct addrinfo *rp = result; rp != nullptr; rp = rp->ai_next) | ||
148 | { | ||
149 | socket_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); | ||
150 | if(socket_fd == -1) | ||
151 | continue; | ||
152 | |||
153 | int err = 0; | ||
154 | if(server) | ||
155 | err = bind(socket_fd, rp->ai_addr, rp->ai_addrlen); | ||
156 | else | ||
157 | err = connect(socket_fd, rp->ai_addr, rp->ai_addrlen); | ||
158 | if(err < 0) | ||
159 | { | ||
160 | close(socket_fd); | ||
161 | socket_fd = -1; | ||
162 | } | ||
163 | else | ||
164 | break; /* success */ | ||
165 | } | ||
166 | /* no address was tried */ | ||
167 | if(socket_fd < 0 && error) | ||
168 | *error = "getaddrinfo() returned no usable result (socket()/connect()/bind() failed)"; | ||
169 | return socket_fd; | ||
170 | } | ||
171 | } | ||
172 | |||
173 | std::shared_ptr<context> context::create_tcp(const std::string& domain, | ||
174 | const std::string& port, std::string *error) | ||
175 | { | ||
176 | int fd = create_tcp_low(domain, port, false, error); | ||
177 | if(fd >= 0) | ||
178 | return context::create_socket(fd); | ||
179 | else | ||
180 | return std::shared_ptr<context>(); | ||
181 | } | ||
182 | |||
183 | std::shared_ptr<context> context::create_unix(const std::string& path, std::string *error) | ||
184 | { | ||
185 | int fd = create_unix_low(false, path.c_str(), path.size() + 1, true, error); | ||
186 | if(fd >= 0) | ||
187 | return context::create_socket(fd); | ||
188 | else | ||
189 | return std::shared_ptr<context>(); | ||
190 | } | ||
191 | |||
192 | std::shared_ptr<context> context::create_unix_abstract(const std::string& path, std::string *error) | ||
193 | { | ||
194 | std::string fake_path = "#" + path; /* the # will be overriden by 0 */ | ||
195 | int fd = create_unix_low(true, fake_path.c_str(), fake_path.size(), true, error); | ||
196 | if(fd >= 0) | ||
197 | return context::create_socket(fd); | ||
198 | else | ||
199 | return std::shared_ptr<context>(); | ||
200 | } | ||
201 | |||
202 | uint32_t context::from_ctx_dev(ctx_dev_t dev) | ||
203 | { | ||
204 | return (uint32_t)(uintptr_t)dev; /* NOTE safe because it was originally a 32-bit int */ | ||
205 | } | ||
206 | |||
207 | hwstub::context::ctx_dev_t context::to_ctx_dev(uint32_t dev) | ||
208 | { | ||
209 | return (ctx_dev_t)(uintptr_t)dev; /* NOTE assume that sizeof(void *)>=sizeof(uint32_t) */ | ||
210 | } | ||
211 | |||
212 | error context::fetch_device_list(std::vector<ctx_dev_t>& list, void*& ptr) | ||
213 | { | ||
214 | (void) ptr; | ||
215 | delayed_init(); | ||
216 | if(m_state == state::DEAD) | ||
217 | return m_error; | ||
218 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
219 | uint8_t *data = nullptr; | ||
220 | size_t data_sz = 0; | ||
221 | debug() << "[net::ctx] --> GET_DEV_LIST\n"; | ||
222 | error err = send_cmd(HWSERVER_GET_DEV_LIST, args, nullptr, 0, &data, &data_sz); | ||
223 | debug() << "[net::ctx] <-- GET_DEV_LIST "; | ||
224 | if(err != error::SUCCESS) | ||
225 | { | ||
226 | debug() << "failed: " << error_string(err) << "\n"; | ||
227 | return err; | ||
228 | } | ||
229 | /* sanity check on size */ | ||
230 | if(data_sz % 4) | ||
231 | { | ||
232 | debug() << "failed: invalid list size\n"; | ||
233 | delete[] data; | ||
234 | return error::PROTOCOL_ERROR; | ||
235 | } | ||
236 | debug() << "\n"; | ||
237 | list.clear(); | ||
238 | /* each entry is a 32-bit ID in network order size */ | ||
239 | uint32_t *data_list = (uint32_t *)data; | ||
240 | for(size_t i = 0; i < data_sz / 4; i++) | ||
241 | list.push_back(to_ctx_dev(from_net_order(data_list[i]))); | ||
242 | delete[] data; | ||
243 | return error::SUCCESS; | ||
244 | } | ||
245 | |||
246 | void context::destroy_device_list(void *ptr) | ||
247 | { | ||
248 | (void)ptr; | ||
249 | } | ||
250 | |||
251 | error context::create_device(ctx_dev_t dev, std::shared_ptr<hwstub::device>& hwdev) | ||
252 | { | ||
253 | // NOTE: can't use make_shared() because of the protected ctor */ | ||
254 | hwdev.reset(new device(shared_from_this(), from_ctx_dev(dev))); | ||
255 | return error::SUCCESS; | ||
256 | } | ||
257 | |||
258 | bool context::match_device(ctx_dev_t dev, std::shared_ptr<hwstub::device> hwdev) | ||
259 | { | ||
260 | device *udev = dynamic_cast<device*>(hwdev.get()); | ||
261 | return udev != nullptr && udev->device_id() == from_ctx_dev(dev); | ||
262 | } | ||
263 | |||
264 | uint32_t context::to_net_order(uint32_t u) | ||
265 | { | ||
266 | return htonl(u); | ||
267 | } | ||
268 | |||
269 | uint32_t context::from_net_order(uint32_t u) | ||
270 | { | ||
271 | return ntohl(u); | ||
272 | } | ||
273 | |||
274 | error context::send_cmd(uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS], uint8_t *send_data, | ||
275 | size_t send_size, uint8_t **recv_data, size_t *recv_size) | ||
276 | { | ||
277 | /* make sure with have the lock, this function might be called concurrently | ||
278 | * by the different threads */ | ||
279 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
280 | |||
281 | if(m_state == state::DEAD) | ||
282 | return m_error; | ||
283 | /* do a delayed init, unless with are doing a HELLO */ | ||
284 | if(m_state == state::HELLO && cmd != HWSERVER_HELLO) | ||
285 | delayed_init(); | ||
286 | /* build header */ | ||
287 | struct hwstub_net_hdr_t hdr; | ||
288 | hdr.magic = to_net_order(HWSERVER_MAGIC); | ||
289 | hdr.cmd = to_net_order(cmd); | ||
290 | for(size_t i = 0; i < HWSTUB_NET_ARGS; i++) | ||
291 | hdr.args[i] = to_net_order(args[i]); | ||
292 | hdr.length = to_net_order((uint32_t)send_size); | ||
293 | /* send header */ | ||
294 | size_t sz = sizeof(hdr); | ||
295 | error err = send((void *)&hdr, sz); | ||
296 | if(err != error::SUCCESS) | ||
297 | { | ||
298 | m_state = state::DEAD; | ||
299 | m_error = err; | ||
300 | return err; | ||
301 | } | ||
302 | if(sz != sizeof(hdr)) | ||
303 | { | ||
304 | m_state = state::DEAD; | ||
305 | m_error = error::PROTOCOL_ERROR; | ||
306 | } | ||
307 | /* send data */ | ||
308 | if(send_size > 0) | ||
309 | { | ||
310 | sz = send_size; | ||
311 | err = send((void *)send_data, sz); | ||
312 | if(err != error::SUCCESS) | ||
313 | { | ||
314 | m_state = state::DEAD; | ||
315 | m_error = err; | ||
316 | return err; | ||
317 | } | ||
318 | if(sz != send_size) | ||
319 | { | ||
320 | m_state = state::DEAD; | ||
321 | m_error = error::PROTOCOL_ERROR; | ||
322 | } | ||
323 | } | ||
324 | /* receive header */ | ||
325 | sz = sizeof(hdr); | ||
326 | err = recv((void *)&hdr, sz); | ||
327 | if(err != error::SUCCESS) | ||
328 | { | ||
329 | m_state = state::DEAD; | ||
330 | m_error = err; | ||
331 | return err; | ||
332 | } | ||
333 | if(sz != sizeof(hdr)) | ||
334 | { | ||
335 | m_state = state::DEAD; | ||
336 | m_error = error::PROTOCOL_ERROR; | ||
337 | return m_error; | ||
338 | } | ||
339 | /* correct byte order */ | ||
340 | hdr.magic = from_net_order(hdr.magic); | ||
341 | hdr.cmd = from_net_order(hdr.cmd); | ||
342 | hdr.length = from_net_order(hdr.length); | ||
343 | /* copy arguments */ | ||
344 | for(size_t i = 0; i < HWSTUB_NET_ARGS; i++) | ||
345 | args[i] = from_net_order(hdr.args[i]); | ||
346 | /* check header */ | ||
347 | if(hdr.magic != HWSERVER_MAGIC) | ||
348 | { | ||
349 | m_state = state::DEAD; | ||
350 | m_error = error::PROTOCOL_ERROR; | ||
351 | return m_error; | ||
352 | } | ||
353 | /* check NACK */ | ||
354 | if(hdr.cmd == HWSERVER_NACK(cmd)) | ||
355 | { | ||
356 | /* translate error */ | ||
357 | switch(args[0]) | ||
358 | { | ||
359 | case HWERR_FAIL: err = error::ERROR; break; | ||
360 | case HWERR_INVALID_ID: err = error::ERROR; break; /* should not happen */ | ||
361 | case HWERR_DISCONNECTED: err = error::DISCONNECTED; break; | ||
362 | } | ||
363 | return err; | ||
364 | } | ||
365 | /* check not ACK */ | ||
366 | if(hdr.cmd != HWSERVER_ACK(cmd)) | ||
367 | { | ||
368 | m_state = state::DEAD; | ||
369 | m_error = error::PROTOCOL_ERROR; | ||
370 | return m_error; | ||
371 | } | ||
372 | /* receive additional data */ | ||
373 | uint8_t *data = nullptr; | ||
374 | if(hdr.length > 0) | ||
375 | { | ||
376 | data = new uint8_t[hdr.length]; | ||
377 | sz = hdr.length; | ||
378 | err = recv((void *)data, sz); | ||
379 | if(err != error::SUCCESS) | ||
380 | { | ||
381 | m_state = state::DEAD; | ||
382 | m_error = err; | ||
383 | return err; | ||
384 | } | ||
385 | if(sz != hdr.length) | ||
386 | { | ||
387 | m_state = state::DEAD; | ||
388 | m_error = error::PROTOCOL_ERROR; | ||
389 | return m_error; | ||
390 | } | ||
391 | } | ||
392 | /* copy data if user want it */ | ||
393 | if(recv_data) | ||
394 | { | ||
395 | if(*recv_data == nullptr) | ||
396 | { | ||
397 | *recv_data = data; | ||
398 | *recv_size = hdr.length; | ||
399 | } | ||
400 | else if(*recv_size < hdr.length) | ||
401 | { | ||
402 | delete[] data; | ||
403 | return error::OVERFLW; | ||
404 | } | ||
405 | else | ||
406 | { | ||
407 | *recv_size = hdr.length; | ||
408 | memcpy(*recv_data, data, *recv_size); | ||
409 | delete[] data; | ||
410 | } | ||
411 | } | ||
412 | /* throw it away otherwise */ | ||
413 | else | ||
414 | { | ||
415 | delete[] data; | ||
416 | } | ||
417 | return error::SUCCESS; | ||
418 | } | ||
419 | |||
420 | void context::delayed_init() | ||
421 | { | ||
422 | /* only do HELLO if we haven't do it yet */ | ||
423 | if(m_state != state::HELLO) | ||
424 | return; | ||
425 | debug() << "[net::ctx] --> HELLO " << HWSTUB_VERSION_MAJOR << "." | ||
426 | << HWSTUB_VERSION_MINOR << "\n"; | ||
427 | /* send HELLO with our version and see what the server is up to */ | ||
428 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
429 | args[0] = HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR; | ||
430 | error err = send_cmd(HWSERVER_HELLO, args, nullptr, 0, nullptr, nullptr); | ||
431 | if(err != error::SUCCESS) | ||
432 | { | ||
433 | debug() << "[net::ctx] <-- HELLO failed: " << error_string(err) << "\n"; | ||
434 | m_state = state::DEAD; | ||
435 | m_error = err; | ||
436 | return; | ||
437 | } | ||
438 | /* check the server is running the same version */ | ||
439 | debug() << "[net::ctx] <-- HELLO " << ((args[0] & 0xff00) >> 8) << "." << (args[0] & 0xff) << ""; | ||
440 | if(args[0] != (HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR)) | ||
441 | { | ||
442 | debug() << " (mismatch)\n"; | ||
443 | m_state = state::DEAD; | ||
444 | m_error = error::SERVER_MISMATCH; | ||
445 | } | ||
446 | debug() << " (good)\n"; | ||
447 | /* good, we can now send commands */ | ||
448 | m_state = state::IDLE; | ||
449 | } | ||
450 | |||
451 | void context::stop_context() | ||
452 | { | ||
453 | /* make sure with have the lock, this function might be call asynchronously */ | ||
454 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
455 | /* if dead, don't do anything */ | ||
456 | if(m_state == state::DEAD) | ||
457 | return; | ||
458 | /* only send BYE if we are initialized */ | ||
459 | if(m_state == state::IDLE) | ||
460 | { | ||
461 | debug() << "[net::ctx] --> BYE\n"; | ||
462 | /* send BYE */ | ||
463 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
464 | error err = send_cmd(HWSERVER_BYE, args, nullptr, 0, nullptr, nullptr); | ||
465 | if(err != error::SUCCESS) | ||
466 | { | ||
467 | debug() << "[net::ctx] <-- BYE failed: " << error_string(err) << "\n"; | ||
468 | m_state = state::DEAD; | ||
469 | m_error = err; | ||
470 | return; | ||
471 | } | ||
472 | debug() << "[net::ctx] <-- BYE\n"; | ||
473 | } | ||
474 | /* now we are dead */ | ||
475 | m_state = state::DEAD; | ||
476 | m_error = error::SERVER_DISCONNECTED; | ||
477 | } | ||
478 | |||
479 | /** | ||
480 | * Socket context | ||
481 | */ | ||
482 | socket_context::socket_context(int socket_fd) | ||
483 | :m_socketfd(socket_fd) | ||
484 | { | ||
485 | set_timeout(std::chrono::milliseconds(1000)); | ||
486 | } | ||
487 | |||
488 | socket_context::~socket_context() | ||
489 | { | ||
490 | stop_context(); | ||
491 | close(m_socketfd); | ||
492 | } | ||
493 | |||
494 | void socket_context::set_timeout(std::chrono::milliseconds ms) | ||
495 | { | ||
496 | struct timeval tv; | ||
497 | tv.tv_usec = 1000 * (ms.count() % 1000); | ||
498 | tv.tv_sec = ms.count() / 1000; | ||
499 | /* set timeout for the client operations */ | ||
500 | setsockopt(m_socketfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv)); | ||
501 | setsockopt(m_socketfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&tv, sizeof(tv)); | ||
502 | } | ||
503 | |||
504 | error socket_context::send(void *buffer, size_t& sz) | ||
505 | { | ||
506 | debug() << "[net::ctx::sock] send(" << sz << "): "; | ||
507 | int ret = ::send(m_socketfd, buffer, sz, MSG_NOSIGNAL); | ||
508 | if(ret >= 0) | ||
509 | { | ||
510 | debug() << "good(" << ret << ")\n"; | ||
511 | sz = (size_t)ret; | ||
512 | return error::SUCCESS; | ||
513 | } | ||
514 | /* convert some errors */ | ||
515 | debug() << "fail(" << errno << "," << strerror(errno) << ")\n"; | ||
516 | switch(errno) | ||
517 | { | ||
518 | #if EAGAIN != EWOULDBLOCK | ||
519 | case EAGAIN: | ||
520 | #endif | ||
521 | case EWOULDBLOCK: return error::TIMEOUT; | ||
522 | case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED; | ||
523 | default: return error::NET_ERROR; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | error socket_context::recv(void *buffer, size_t& sz) | ||
528 | { | ||
529 | debug() << "[net::ctx::sock] recv(" << sz << "): "; | ||
530 | int ret = ::recv(m_socketfd, buffer, sz, MSG_WAITALL); | ||
531 | if(ret > 0) | ||
532 | { | ||
533 | debug() << "good(" << ret << ")\n"; | ||
534 | sz = (size_t)ret; | ||
535 | return error::SUCCESS; | ||
536 | } | ||
537 | if(ret == 0) | ||
538 | { | ||
539 | debug() << "disconnected\n"; | ||
540 | return error::SERVER_DISCONNECTED; | ||
541 | } | ||
542 | debug() << "fail(" << errno << "," << strerror(errno) << ")\n"; | ||
543 | switch(errno) | ||
544 | { | ||
545 | #if EAGAIN != EWOULDBLOCK | ||
546 | case EAGAIN: | ||
547 | #endif | ||
548 | case EWOULDBLOCK: return error::TIMEOUT; | ||
549 | default: return error::NET_ERROR; | ||
550 | } | ||
551 | } | ||
552 | |||
553 | /** | ||
554 | * Device | ||
555 | */ | ||
556 | device::device(std::shared_ptr<hwstub::context> ctx, uint32_t devid) | ||
557 | :hwstub::device(ctx), m_device_id(devid) | ||
558 | { | ||
559 | } | ||
560 | |||
561 | device::~device() | ||
562 | { | ||
563 | } | ||
564 | |||
565 | uint32_t device::device_id() | ||
566 | { | ||
567 | return m_device_id; | ||
568 | } | ||
569 | |||
570 | error device::open_dev(std::shared_ptr<hwstub::handle>& handle) | ||
571 | { | ||
572 | std::shared_ptr<hwstub::context> hctx = get_context(); | ||
573 | if(!hctx) | ||
574 | return error::NO_CONTEXT; | ||
575 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
576 | ctx->debug() << "[net::dev] --> DEV_OPEN(" << m_device_id << ")\n"; | ||
577 | /* ask the server to open the device, note that the device ID may not exists | ||
578 | * anymore */ | ||
579 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
580 | args[0] = m_device_id; | ||
581 | error err = ctx->send_cmd(HWSERVER_DEV_OPEN, args, nullptr, 0, nullptr, nullptr); | ||
582 | if(err != error::SUCCESS) | ||
583 | { | ||
584 | ctx->debug() << "[net::ctx::dev] <-- DEV_OPEN failed: " << error_string(err) << "\n"; | ||
585 | return err; | ||
586 | } | ||
587 | ctx->debug() << "[net::ctx::dev] <-- DEV_OPEN: handle = " << args[0] << "\n"; | ||
588 | // NOTE: can't use make_shared() because of the protected ctor */ | ||
589 | handle.reset(new hwstub::net::handle(shared_from_this(), args[0])); | ||
590 | return error::SUCCESS; | ||
591 | } | ||
592 | |||
593 | bool device::has_multiple_open() const | ||
594 | { | ||
595 | return false; | ||
596 | } | ||
597 | |||
598 | /** | ||
599 | * Handle | ||
600 | */ | ||
601 | handle::handle(std::shared_ptr<hwstub::device> dev, uint32_t hid) | ||
602 | :hwstub::handle(dev), m_handle_id(hid) | ||
603 | { | ||
604 | } | ||
605 | |||
606 | handle::~handle() | ||
607 | { | ||
608 | /* try to close the handle, if context is still accessible */ | ||
609 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
610 | if(hctx) | ||
611 | { | ||
612 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
613 | ctx->debug() << "[net::handle] --> DEV_CLOSE(" << m_handle_id << ")\n"; | ||
614 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
615 | args[0] = m_handle_id; | ||
616 | error err = ctx->send_cmd(HWSERVER_DEV_CLOSE, args, nullptr, 0, nullptr, nullptr); | ||
617 | if(err != error::SUCCESS) | ||
618 | ctx->debug() << "[net::handle] <-- DEV_CLOSE failed: " << error_string(err) << "\n"; | ||
619 | else | ||
620 | ctx->debug() << "[net::handle] <-- DEV_CLOSE\n"; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | error handle::read_dev(uint32_t addr, void *buf, size_t& sz, bool atomic) | ||
625 | { | ||
626 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
627 | if(!hctx) | ||
628 | return error::NO_CONTEXT; | ||
629 | |||
630 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
631 | ctx->debug() << "[net::handle] --> READ(" << m_handle_id << ",0x" << std::hex | ||
632 | << addr << "," << sz << "," << atomic << ")\n"; | ||
633 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
634 | args[0] = m_handle_id; | ||
635 | args[1] = addr; | ||
636 | args[2] = sz; | ||
637 | args[3] = atomic ? HWSERVER_RW_ATOMIC : 0; | ||
638 | error err = ctx->send_cmd(HWSERVER_READ, args, nullptr, 0, (uint8_t **)&buf, &sz); | ||
639 | if(err != error::SUCCESS) | ||
640 | { | ||
641 | ctx->debug() << "[net::handle] <-- READ failed: " << error_string(err) << "\n"; | ||
642 | return err; | ||
643 | } | ||
644 | ctx->debug() << "[net::handle] <-- READ\n"; | ||
645 | return error::SUCCESS; | ||
646 | } | ||
647 | |||
648 | error handle::write_dev(uint32_t addr, const void *buf, size_t& sz, bool atomic) | ||
649 | { | ||
650 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
651 | if(!hctx) | ||
652 | return error::NO_CONTEXT; | ||
653 | |||
654 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
655 | ctx->debug() << "[net::handle] --> WRITE(" << m_handle_id << ",0x" << std::hex | ||
656 | << addr << "," << sz << "," << atomic << ")\n"; | ||
657 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
658 | args[0] = m_handle_id; | ||
659 | args[1] = addr; | ||
660 | args[2] = atomic ? HWSERVER_RW_ATOMIC : 0; | ||
661 | error err = ctx->send_cmd(HWSERVER_WRITE, args, (uint8_t *)buf, sz, nullptr, nullptr); | ||
662 | if(err != error::SUCCESS) | ||
663 | { | ||
664 | ctx->debug() << "[net::handle] <-- WRITE failed: " << error_string(err) << "\n"; | ||
665 | return err; | ||
666 | } | ||
667 | ctx->debug() << "[net::handle] <-- WRITE\n"; | ||
668 | return error::SUCCESS; | ||
669 | } | ||
670 | |||
671 | error handle::get_dev_desc(uint16_t desc, void *buf, size_t& buf_sz) | ||
672 | { | ||
673 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
674 | if(!hctx) | ||
675 | return error::NO_CONTEXT; | ||
676 | |||
677 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
678 | ctx->debug() << "[net::handle] --> GET_DESC(" << m_handle_id << ",0x" << std::hex | ||
679 | << desc << "," << buf_sz << ")\n"; | ||
680 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
681 | args[0] = m_handle_id; | ||
682 | args[1] = desc; | ||
683 | args[2] = buf_sz; | ||
684 | error err = ctx->send_cmd(HWSERVER_GET_DESC, args, nullptr, 0, (uint8_t **)&buf, &buf_sz); | ||
685 | if(err != error::SUCCESS) | ||
686 | { | ||
687 | ctx->debug() << "[net::handle] <-- GET_DESC failed: " << error_string(err) << "\n"; | ||
688 | return err; | ||
689 | } | ||
690 | ctx->debug() << "[net::handle] <-- GET_DESC\n"; | ||
691 | return error::SUCCESS; | ||
692 | } | ||
693 | |||
694 | error handle::get_dev_log(void *buf, size_t& buf_sz) | ||
695 | { | ||
696 | std::shared_ptr<hwstub::context> hctx = get_device()->get_context(); | ||
697 | if(!hctx) | ||
698 | return error::NO_CONTEXT; | ||
699 | |||
700 | context *ctx = dynamic_cast<context*>(hctx.get()); | ||
701 | ctx->debug() << "[net::handle] --> GET_LOG(" << buf_sz << ")\n"; | ||
702 | uint32_t args[HWSTUB_NET_ARGS] = {0}; | ||
703 | args[0] = m_handle_id; | ||
704 | args[1] = buf_sz; | ||
705 | error err = ctx->send_cmd(HWSERVER_GET_LOG, args, nullptr, 0, (uint8_t **)&buf, &buf_sz); | ||
706 | if(err != error::SUCCESS) | ||
707 | { | ||
708 | ctx->debug() << "[net::handle] <-- GET_LOG failed: " << error_string(err) << "\n"; | ||
709 | return err; | ||
710 | } | ||
711 | ctx->debug() << "[net::handle] <-- GET_LOG\n"; | ||
712 | return error::SUCCESS; | ||
713 | } | ||
714 | |||
715 | error handle::exec_dev(uint32_t addr, uint16_t flags) | ||
716 | { | ||
717 | (void) addr; | ||
718 | (void) flags; | ||
719 | return error::DUMMY; | ||
720 | } | ||
721 | |||
722 | error handle::status() const | ||
723 | { | ||
724 | return hwstub::handle::status(); | ||
725 | } | ||
726 | |||
727 | size_t handle::get_buffer_size() | ||
728 | { | ||
729 | return 2048; | ||
730 | } | ||
731 | |||
732 | /** | ||
733 | * Server | ||
734 | */ | ||
735 | server::server(std::shared_ptr<hwstub::context> ctx) | ||
736 | :m_context(ctx) | ||
737 | { | ||
738 | clear_debug(); | ||
739 | } | ||
740 | |||
741 | server::~server() | ||
742 | { | ||
743 | } | ||
744 | |||
745 | void server::stop_server() | ||
746 | { | ||
747 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
748 | /* ask all client threads to stop */ | ||
749 | for(auto& cl : m_client) | ||
750 | cl.exit = true; | ||
751 | /* wait for each thread to stop */ | ||
752 | for(auto& cl : m_client) | ||
753 | cl.future.wait(); | ||
754 | } | ||
755 | |||
756 | std::shared_ptr<server> server::create_unix(std::shared_ptr<hwstub::context> ctx, | ||
757 | const std::string& path, std::string *error) | ||
758 | { | ||
759 | int fd = create_unix_low(false, path.c_str(), path.size() + 1, false, error); | ||
760 | if(fd >= 0) | ||
761 | return socket_server::create_socket(ctx, fd); | ||
762 | else | ||
763 | return std::shared_ptr<server>(); | ||
764 | } | ||
765 | |||
766 | std::shared_ptr<server> server::create_unix_abstract(std::shared_ptr<hwstub::context> ctx, | ||
767 | const std::string& path, std::string *error) | ||
768 | { | ||
769 | std::string fake_path = "#" + path; /* the # will be overriden by 0 */ | ||
770 | int fd = create_unix_low(true, fake_path.c_str(), fake_path.size(), false, error); | ||
771 | if(fd >= 0) | ||
772 | return socket_server::create_socket(ctx, fd); | ||
773 | else | ||
774 | return std::shared_ptr<server>(); | ||
775 | } | ||
776 | |||
777 | std::shared_ptr<server> server::create_socket(std::shared_ptr<hwstub::context> ctx, | ||
778 | int socket_fd) | ||
779 | { | ||
780 | return socket_server::create(ctx, socket_fd); | ||
781 | } | ||
782 | |||
783 | std::shared_ptr<server> server::create_tcp(std::shared_ptr<hwstub::context> ctx, | ||
784 | const std::string& domain, const std::string& port, std::string *error) | ||
785 | { | ||
786 | int fd = create_tcp_low(domain, port, true, error); | ||
787 | if(fd >= 0) | ||
788 | return socket_server::create_socket(ctx, fd); | ||
789 | else | ||
790 | return std::shared_ptr<server>(); | ||
791 | } | ||
792 | |||
793 | void server::set_debug(std::ostream& os) | ||
794 | { | ||
795 | m_debug = &os; | ||
796 | } | ||
797 | |||
798 | std::ostream& server::debug() | ||
799 | { | ||
800 | return *m_debug; | ||
801 | } | ||
802 | |||
803 | server::client_state::client_state(srv_client_t cl, std::future<void>&& f) | ||
804 | :client(cl), future(std::move(f)), exit(false), next_dev_id(42), | ||
805 | next_handle_id(19) | ||
806 | { | ||
807 | } | ||
808 | |||
809 | void server::client_thread2(server *s, client_state *cs) | ||
810 | { | ||
811 | s->client_thread(cs); | ||
812 | } | ||
813 | |||
814 | uint32_t server::to_net_order(uint32_t u) | ||
815 | { | ||
816 | return htonl(u); | ||
817 | } | ||
818 | |||
819 | uint32_t server::from_net_order(uint32_t u) | ||
820 | { | ||
821 | return ntohl(u); | ||
822 | } | ||
823 | |||
824 | void server::client_thread(client_state *state) | ||
825 | { | ||
826 | debug() << "[net::srv::client] start: " << state->client << "\n"; | ||
827 | while(!state->exit) | ||
828 | { | ||
829 | /* wait for some header */ | ||
830 | struct hwstub_net_hdr_t hdr; | ||
831 | size_t sz = sizeof(hdr); | ||
832 | error err; | ||
833 | /* wait for some command, or exit flag */ | ||
834 | do | ||
835 | err = recv(state->client, (void *)&hdr, sz); | ||
836 | while(err == error::TIMEOUT && !state->exit); | ||
837 | if(state->exit || err != error::SUCCESS || sz != sizeof(hdr)) | ||
838 | break; | ||
839 | /* convert to host order */ | ||
840 | hdr.magic = from_net_order(hdr.magic); | ||
841 | hdr.cmd = from_net_order(hdr.cmd); | ||
842 | hdr.length = from_net_order(hdr.length); | ||
843 | /* copy arguments */ | ||
844 | for(size_t i = 0; i < HWSTUB_NET_ARGS; i++) | ||
845 | hdr.args[i] = from_net_order(hdr.args[i]); | ||
846 | /* check header */ | ||
847 | if(hdr.magic != HWSERVER_MAGIC) | ||
848 | break; | ||
849 | /* receive data | ||
850 | * FIXME check length here */ | ||
851 | uint8_t *data = nullptr; | ||
852 | if(hdr.length > 0) | ||
853 | { | ||
854 | data = new uint8_t[hdr.length]; | ||
855 | sz = hdr.length; | ||
856 | /* wait for some command, or exit flag */ | ||
857 | do | ||
858 | err = recv(state->client, (void *)data, sz); | ||
859 | while(err == error::TIMEOUT && !state->exit); | ||
860 | if(state->exit || err != error::SUCCESS || sz != hdr.length) | ||
861 | { | ||
862 | delete[] data; | ||
863 | break; | ||
864 | } | ||
865 | } | ||
866 | /* hande command */ | ||
867 | uint8_t *send_data = nullptr; | ||
868 | size_t send_size = 0; | ||
869 | err = handle_cmd(state, hdr.cmd, hdr.args, data, hdr.length, | ||
870 | send_data, send_size); | ||
871 | /* free data */ | ||
872 | delete[] data; | ||
873 | /* construct header */ | ||
874 | if(err != error::SUCCESS) | ||
875 | { | ||
876 | hdr.magic = to_net_order(HWSERVER_MAGIC); | ||
877 | hdr.cmd = to_net_order(HWSERVER_NACK(hdr.cmd)); | ||
878 | hdr.length = to_net_order(0); | ||
879 | hdr.args[0] = to_net_order(HWERR_FAIL); | ||
880 | for(size_t i = 1; i < HWSTUB_NET_ARGS; i++) | ||
881 | hdr.args[i] = to_net_order(0); | ||
882 | send_size = 0; | ||
883 | } | ||
884 | else | ||
885 | { | ||
886 | hdr.magic = to_net_order(HWSERVER_MAGIC); | ||
887 | hdr.cmd = to_net_order(HWSERVER_ACK(hdr.cmd)); | ||
888 | hdr.length = to_net_order(send_size); | ||
889 | for(size_t i = 0; i < HWSTUB_NET_ARGS; i++) | ||
890 | hdr.args[i] = to_net_order(hdr.args[i]); | ||
891 | } | ||
892 | /* send header */ | ||
893 | sz = sizeof(hdr); | ||
894 | do | ||
895 | err = send(state->client, (void *)&hdr, sz); | ||
896 | while(err == error::TIMEOUT && !state->exit); | ||
897 | if(state->exit || err != error::SUCCESS || sz != sizeof(hdr)) | ||
898 | { | ||
899 | delete[] send_data; | ||
900 | break; | ||
901 | } | ||
902 | /* send data if there is some */ | ||
903 | if(send_size > 0) | ||
904 | { | ||
905 | sz = send_size; | ||
906 | do | ||
907 | err = send(state->client, (void *)send_data, sz); | ||
908 | while(err == error::TIMEOUT && !state->exit); | ||
909 | delete[] send_data; | ||
910 | if(state->exit || err != error::SUCCESS || sz != send_size) | ||
911 | break; | ||
912 | } | ||
913 | } | ||
914 | debug() << "[net::srv::client] stop: " << state->client << "\n"; | ||
915 | /* clean client state to avoiding keeping references to objets */ | ||
916 | state->dev_map.clear(); | ||
917 | state->handle_map.clear(); | ||
918 | /* kill client */ | ||
919 | terminate_client(state->client); | ||
920 | } | ||
921 | |||
922 | void server::client_arrived(srv_client_t client) | ||
923 | { | ||
924 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
925 | debug() << "[net::srv] client arrived: " << client << "\n"; | ||
926 | /* the naive way would be to use a std::thread but this class is annoying | ||
927 | * because it is impossible to check if a thread has exited, except by calling | ||
928 | * join() which is blocking. Fortunately, std::packaged_task and std::future | ||
929 | * provide a way around this */ | ||
930 | |||
931 | std::packaged_task<void(server*, client_state*)> task(&server::client_thread2); | ||
932 | m_client.emplace_back(client, task.get_future()); | ||
933 | std::thread(std::move(task), this, &m_client.back()).detach(); | ||
934 | } | ||
935 | |||
936 | void server::client_left(srv_client_t client) | ||
937 | { | ||
938 | std::unique_lock<std::recursive_mutex> lock(m_mutex); | ||
939 | debug() << "[net::srv] client left: " << client << "\n"; | ||
940 | /* find thread and set its exit flag, also cleanup threads that finished */ | ||
941 | for(auto it = m_client.begin(); it != m_client.end();) | ||
942 | { | ||
943 | /* check if thread has finished */ | ||
944 | if(it->future.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready) | ||
945 | { | ||
946 | it = m_client.erase(it); | ||
947 | continue; | ||
948 | } | ||
949 | /* set exit flag if this our thread */ | ||
950 | if(it->client == client) | ||
951 | it->exit = true; | ||
952 | ++it; | ||
953 | } | ||
954 | } | ||
955 | |||
956 | error server::handle_cmd(client_state *state, uint32_t cmd, uint32_t args[HWSTUB_NET_ARGS], | ||
957 | uint8_t *recv_data, size_t recv_size, uint8_t*& send_data, size_t& send_size) | ||
958 | { | ||
959 | send_data = nullptr; | ||
960 | send_size = 0; | ||
961 | /* NOTE: commands are serialized by the client thread, this function is thus | ||
962 | * thread safe WITH RESPECT TO CLIENT DATA. If you need to use global data here, | ||
963 | * protect it by a mutex or make sure it is safe (hwstub context is thread-safe) */ | ||
964 | |||
965 | /* HELLO */ | ||
966 | if(cmd == HWSERVER_HELLO) | ||
967 | { | ||
968 | debug() << "[net::srv::cmd] --> HELLO " << ((args[0] & 0xff00) >> 8) | ||
969 | << "." << (args[0] & 0xff); | ||
970 | if(args[0] != (HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR)) | ||
971 | { | ||
972 | debug() << " (mismatch)\n"; | ||
973 | return error::ERROR; | ||
974 | } | ||
975 | debug() << " (good)\n"; | ||
976 | debug() << "[net::srv::cmd] <-- HELLO " << HWSTUB_VERSION_MAJOR << "." | ||
977 | << HWSTUB_VERSION_MINOR << "\n"; | ||
978 | /* send HELLO with our version */ | ||
979 | args[0] = HWSTUB_VERSION_MAJOR << 8 | HWSTUB_VERSION_MINOR; | ||
980 | return error::SUCCESS; | ||
981 | } | ||
982 | /* BYE */ | ||
983 | else if(cmd == HWSERVER_BYE) | ||
984 | { | ||
985 | debug() << "[net::srv::cmd] --> BYE\n"; | ||
986 | /* ask client thread to exit after this */ | ||
987 | state->exit = true; | ||
988 | debug() << "[net::srv::cmd] <-- BYE\n"; | ||
989 | return error::SUCCESS; | ||
990 | } | ||
991 | /* GET_DEV_LIST */ | ||
992 | else if(cmd == HWSERVER_GET_DEV_LIST) | ||
993 | { | ||
994 | debug() << "[net::srv::cmd] --> GET_DEV_LIST\n"; | ||
995 | /* fetch list again */ | ||
996 | std::vector<std::shared_ptr<hwstub::device>> list; | ||
997 | error err = m_context->get_device_list(list); | ||
998 | if(err != error::SUCCESS) | ||
999 | { | ||
1000 | debug() << "[net::srv::cmd] cannot fetch list: " << hwstub::error_string(err) << "\n"; | ||
1001 | debug() << "[net::srv::cmd] <-- GET_DEV_LIST (error)\n"; | ||
1002 | return err; | ||
1003 | } | ||
1004 | /* update list: drop device that left */ | ||
1005 | std::vector<uint32_t> to_drop; | ||
1006 | for(auto it : state->dev_map) | ||
1007 | { | ||
1008 | bool still_there = false; | ||
1009 | /* this has quadratic complexity, optimize this if needed */ | ||
1010 | for(auto dev : list) | ||
1011 | if(it.second == dev) | ||
1012 | still_there = true; | ||
1013 | if(!still_there) | ||
1014 | to_drop.push_back(it.first); | ||
1015 | } | ||
1016 | for(auto id : to_drop) | ||
1017 | state->dev_map.erase(state->dev_map.find(id)); | ||
1018 | /* add new devices */ | ||
1019 | std::vector<std::shared_ptr<hwstub::device>> to_add; | ||
1020 | for(auto dev : list) | ||
1021 | { | ||
1022 | bool already_there = false; | ||
1023 | for(auto it : state->dev_map) | ||
1024 | if(it.second == dev) | ||
1025 | already_there = true; | ||
1026 | if(!already_there) | ||
1027 | to_add.push_back(dev); | ||
1028 | } | ||
1029 | for(auto dev : to_add) | ||
1030 | state->dev_map[state->next_dev_id++] = dev; | ||
1031 | /* create response list */ | ||
1032 | send_size = sizeof(uint32_t) * state->dev_map.size(); | ||
1033 | send_data = new uint8_t[send_size]; | ||
1034 | uint32_t *p = (uint32_t *)send_data; | ||
1035 | for(auto it : state->dev_map) | ||
1036 | *p++ = to_net_order(it.first); | ||
1037 | debug() << "[net::srv::cmd] <-- GET_DEV_LIST\n"; | ||
1038 | return error::SUCCESS; | ||
1039 | } | ||
1040 | /* DEV_OPEN */ | ||
1041 | else if(cmd == HWSERVER_DEV_OPEN) | ||
1042 | { | ||
1043 | uint32_t devid = args[0]; | ||
1044 | debug() << "[net::srv::cmd] --> DEV_OPEN(" << devid << ")\n"; | ||
1045 | /* check ID is valid */ | ||
1046 | auto it = state->dev_map.find(devid); | ||
1047 | if(it == state->dev_map.end()) | ||
1048 | { | ||
1049 | debug() << "[net::srv::cmd] unknwon device ID\n"; | ||
1050 | debug() << "[net::srv::cmd] <-- DEV_OPEN (error)\n"; | ||
1051 | return error::ERROR; | ||
1052 | } | ||
1053 | /* good, now try to get a handle */ | ||
1054 | std::shared_ptr<hwstub::handle> handle; | ||
1055 | error err = it->second->open(handle); | ||
1056 | if(err != error::SUCCESS) | ||
1057 | { | ||
1058 | debug() << "[net::srv::cmd] cannot open device: " << hwstub::error_string(err) << "\n"; | ||
1059 | return err; | ||
1060 | } | ||
1061 | /* record ID and return it */ | ||
1062 | args[0] = state->next_handle_id; | ||
1063 | state->handle_map[state->next_handle_id++] = handle; | ||
1064 | return error::SUCCESS; | ||
1065 | } | ||
1066 | /* DEV_CLOSE */ | ||
1067 | else if(cmd == HWSERVER_DEV_CLOSE) | ||
1068 | { | ||
1069 | uint32_t hid = args[0]; | ||
1070 | debug() << "[net::srv::cmd] --> DEV_CLOSE(" << hid << ")\n"; | ||
1071 | /* check ID is valid */ | ||
1072 | auto it = state->handle_map.find(hid); | ||
1073 | if(it == state->handle_map.end()) | ||
1074 | { | ||
1075 | debug() << "[net::srv::cmd] unknwon handle ID\n"; | ||
1076 | debug() << "[net::srv::cmd] <-- DEV_CLOSE (error)\n"; | ||
1077 | return error::ERROR; | ||
1078 | } | ||
1079 | /* release ID and handle */ | ||
1080 | state->handle_map.erase(it); | ||
1081 | debug() << "[net::srv::cmd] <-- DEV_CLOSE\n"; | ||
1082 | return error::SUCCESS; | ||
1083 | } | ||
1084 | /* HWSERVER_GET_DESC */ | ||
1085 | else if(cmd == HWSERVER_GET_DESC) | ||
1086 | { | ||
1087 | uint32_t hid = args[0]; | ||
1088 | uint32_t did = args[1]; | ||
1089 | uint32_t len = args[2]; | ||
1090 | debug() << "[net::srv::cmd] --> GET_DESC(" << hid << ",0x" << std::hex << did | ||
1091 | << "," << len << ")\n"; | ||
1092 | /* check ID is valid */ | ||
1093 | auto it = state->handle_map.find(hid); | ||
1094 | if(it == state->handle_map.end()) | ||
1095 | { | ||
1096 | debug() << "[net::srv::cmd] unknown handle ID\n"; | ||
1097 | debug() << "[net::srv::cmd] <-- GET_DESC (error)\n"; | ||
1098 | return error::ERROR; | ||
1099 | } | ||
1100 | /* query desc */ | ||
1101 | send_size = len; | ||
1102 | send_data = new uint8_t[send_size]; | ||
1103 | error err = it->second->get_desc(did, send_data, send_size); | ||
1104 | if(err != error::SUCCESS) | ||
1105 | { | ||
1106 | delete[] send_data; | ||
1107 | debug() << "[net::srv::cmd] cannot get descriptor: " << error_string(err) << "\n"; | ||
1108 | debug() << "[net::srv::cmd] <-- GET_DESC (error)\n"; | ||
1109 | return err; | ||
1110 | } | ||
1111 | debug() << "[net::srv::cmd] <-- GET_DESC\n"; | ||
1112 | return error::SUCCESS; | ||
1113 | } | ||
1114 | /* HWSERVER_GET_LOG */ | ||
1115 | else if(cmd == HWSERVER_GET_LOG) | ||
1116 | { | ||
1117 | uint32_t hid = args[0]; | ||
1118 | uint32_t len = args[1]; | ||
1119 | debug() << "[net::srv::cmd] --> GET_LOG(" << hid << "," << len << ")\n"; | ||
1120 | /* check ID is valid */ | ||
1121 | auto it = state->handle_map.find(hid); | ||
1122 | if(it == state->handle_map.end()) | ||
1123 | { | ||
1124 | debug() << "[net::srv::cmd] unknown handle ID\n"; | ||
1125 | debug() << "[net::srv::cmd] <-- GET_DESC (error)\n"; | ||
1126 | return error::ERROR; | ||
1127 | } | ||
1128 | /* query log */ | ||
1129 | send_size = len; | ||
1130 | send_data = new uint8_t[send_size]; | ||
1131 | error err = it->second->get_log(send_data, send_size); | ||
1132 | if(err != error::SUCCESS) | ||
1133 | { | ||
1134 | delete[] send_data; | ||
1135 | debug() << "[net::srv::cmd] cannot get log: " << error_string(err) << "\n"; | ||
1136 | debug() << "[net::srv::cmd] <-- GET_LOG (error)\n"; | ||
1137 | return err; | ||
1138 | } | ||
1139 | if(send_size == 0) | ||
1140 | delete[] send_data; | ||
1141 | debug() << "[net::srv::cmd] <-- GET_LOG\n"; | ||
1142 | return error::SUCCESS; | ||
1143 | } | ||
1144 | /* HWSERVER_READ */ | ||
1145 | else if(cmd == HWSERVER_READ) | ||
1146 | { | ||
1147 | uint32_t hid = args[0]; | ||
1148 | uint32_t addr = args[1]; | ||
1149 | uint32_t len = args[2]; | ||
1150 | uint32_t flags = args[3]; | ||
1151 | debug() << "[net::srv::cmd] --> READ(" << hid << ",0x" << std::hex << addr << "," | ||
1152 | << len << ",0x" << std::hex << flags << ")\n"; | ||
1153 | /* check ID is valid */ | ||
1154 | auto it = state->handle_map.find(hid); | ||
1155 | if(it == state->handle_map.end()) | ||
1156 | { | ||
1157 | debug() << "[net::srv::cmd] unknown handle ID\n"; | ||
1158 | debug() << "[net::srv::cmd] <-- READ (error)\n"; | ||
1159 | return error::ERROR; | ||
1160 | } | ||
1161 | /* read */ | ||
1162 | send_size = len; | ||
1163 | send_data = new uint8_t[send_size]; | ||
1164 | error err = it->second->read(addr, send_data, send_size, !!(flags & HWSERVER_RW_ATOMIC)); | ||
1165 | if(err != error::SUCCESS) | ||
1166 | { | ||
1167 | delete[] send_data; | ||
1168 | debug() << "[net::srv::cmd] cannot read: " << error_string(err) << "\n"; | ||
1169 | debug() << "[net::srv::cmd] <-- READ (error)\n"; | ||
1170 | return err; | ||
1171 | } | ||
1172 | debug() << "[net::srv::cmd] <-- READ\n"; | ||
1173 | return error::SUCCESS; | ||
1174 | } | ||
1175 | /* HWSERVER_WRITE */ | ||
1176 | else if(cmd == HWSERVER_WRITE) | ||
1177 | { | ||
1178 | uint32_t hid = args[0]; | ||
1179 | uint32_t addr = args[1]; | ||
1180 | uint32_t flags = args[2]; | ||
1181 | debug() << "[net::srv::cmd] --> WRITE(" << hid << ",0x" << std::hex << addr << "," | ||
1182 | << recv_size << ",0x" << std::hex << flags << ")\n"; | ||
1183 | /* check ID is valid */ | ||
1184 | auto it = state->handle_map.find(hid); | ||
1185 | if(it == state->handle_map.end()) | ||
1186 | { | ||
1187 | debug() << "[net::srv::cmd] unknown handle ID\n"; | ||
1188 | debug() << "[net::srv::cmd] <-- WRITE (error)\n"; | ||
1189 | return error::ERROR; | ||
1190 | } | ||
1191 | /* write */ | ||
1192 | error err = it->second->write(addr, recv_data, recv_size, !!(flags & HWSERVER_RW_ATOMIC)); | ||
1193 | if(err != error::SUCCESS) | ||
1194 | { | ||
1195 | delete[] send_data; | ||
1196 | debug() << "[net::srv::cmd] cannot write: " << error_string(err) << "\n"; | ||
1197 | debug() << "[net::srv::cmd] <-- WRITE (error)\n"; | ||
1198 | return err; | ||
1199 | } | ||
1200 | debug() << "[net::srv::cmd] <-- WRITE\n"; | ||
1201 | return error::SUCCESS; | ||
1202 | } | ||
1203 | else | ||
1204 | { | ||
1205 | debug() << "[net::srv::cmd] <-> unknown cmd (0x" << std::hex << cmd << ")\n"; | ||
1206 | return error::ERROR; | ||
1207 | } | ||
1208 | } | ||
1209 | |||
1210 | /* | ||
1211 | * Socket server | ||
1212 | */ | ||
1213 | socket_server::socket_server(std::shared_ptr<hwstub::context> contex, int socket_fd) | ||
1214 | :server(contex), m_socketfd(socket_fd) | ||
1215 | { | ||
1216 | m_discovery_exit = false; | ||
1217 | set_timeout(std::chrono::milliseconds(1000)); | ||
1218 | m_discovery_thread = std::thread(&socket_server::discovery_thread1, this); | ||
1219 | } | ||
1220 | |||
1221 | socket_server::~socket_server() | ||
1222 | { | ||
1223 | /* first stop discovery thread to make sure no more clients are created */ | ||
1224 | m_discovery_exit = true; | ||
1225 | m_discovery_thread.join(); | ||
1226 | close(m_socketfd); | ||
1227 | /* ask server to do a clean stop */ | ||
1228 | stop_server(); | ||
1229 | } | ||
1230 | |||
1231 | std::shared_ptr<server> socket_server::create(std::shared_ptr<hwstub::context> ctx, | ||
1232 | int socket_fd) | ||
1233 | { | ||
1234 | // NOTE: can't use make_shared() because of the protected ctor */ | ||
1235 | return std::shared_ptr<socket_server>(new socket_server(ctx, socket_fd)); | ||
1236 | } | ||
1237 | |||
1238 | void socket_server::set_timeout(std::chrono::milliseconds ms) | ||
1239 | { | ||
1240 | m_timeout.tv_usec = 1000 * (ms.count() % 1000); | ||
1241 | m_timeout.tv_sec = ms.count() / 1000; | ||
1242 | } | ||
1243 | |||
1244 | int socket_server::from_srv_client(srv_client_t cli) | ||
1245 | { | ||
1246 | return (int)(intptr_t)cli; | ||
1247 | } | ||
1248 | |||
1249 | socket_server::srv_client_t socket_server::to_srv_client(int fd) | ||
1250 | { | ||
1251 | return (srv_client_t)(intptr_t)fd; | ||
1252 | } | ||
1253 | |||
1254 | void socket_server::discovery_thread1(socket_server *s) | ||
1255 | { | ||
1256 | s->discovery_thread(); | ||
1257 | } | ||
1258 | |||
1259 | void socket_server::terminate_client(srv_client_t client) | ||
1260 | { | ||
1261 | debug() << "[net::srv::sock] terminate client: " << client << "\n"; | ||
1262 | /* simply close connection */ | ||
1263 | close(from_srv_client(client)); | ||
1264 | } | ||
1265 | |||
1266 | error socket_server::send(srv_client_t client, void *buffer, size_t& sz) | ||
1267 | { | ||
1268 | debug() << "[net::ctx::sock] send(" << client << ", " << sz << "): "; | ||
1269 | int ret = ::send(from_srv_client(client), buffer, sz, MSG_NOSIGNAL); | ||
1270 | if(ret >= 0) | ||
1271 | { | ||
1272 | debug() << "good(" << ret << ")\n"; | ||
1273 | sz = (size_t)ret; | ||
1274 | return error::SUCCESS; | ||
1275 | } | ||
1276 | /* convert some errors */ | ||
1277 | debug() << "fail(" << errno << "," << strerror(errno) << ")\n"; | ||
1278 | switch(errno) | ||
1279 | { | ||
1280 | #if EAGAIN != EWOULDBLOCK | ||
1281 | case EAGAIN: | ||
1282 | #endif | ||
1283 | case EWOULDBLOCK: return error::TIMEOUT; | ||
1284 | case ECONNRESET: case EPIPE: return error::SERVER_DISCONNECTED; | ||
1285 | default: return error::NET_ERROR; | ||
1286 | } | ||
1287 | } | ||
1288 | |||
1289 | error socket_server::recv(srv_client_t client, void *buffer, size_t& sz) | ||
1290 | { | ||
1291 | debug() << "[net::ctx::sock] recv(" << client << ", " << sz << "): "; | ||
1292 | int ret = ::recv(from_srv_client(client), buffer, sz, MSG_WAITALL); | ||
1293 | if(ret > 0) | ||
1294 | { | ||
1295 | debug() << "good(" << ret << ")\n"; | ||
1296 | sz = (size_t)ret; | ||
1297 | return error::SUCCESS; | ||
1298 | } | ||
1299 | if(ret == 0) | ||
1300 | { | ||
1301 | debug() << "disconnected\n"; | ||
1302 | return error::SERVER_DISCONNECTED; | ||
1303 | } | ||
1304 | debug() << "fail(" << errno << "," << strerror(errno) << ")\n"; | ||
1305 | switch(errno) | ||
1306 | { | ||
1307 | #if EAGAIN != EWOULDBLOCK | ||
1308 | case EAGAIN: | ||
1309 | #endif | ||
1310 | case EWOULDBLOCK: return error::TIMEOUT; | ||
1311 | default: return error::NET_ERROR; | ||
1312 | } | ||
1313 | } | ||
1314 | |||
1315 | void socket_server::discovery_thread() | ||
1316 | { | ||
1317 | debug() << "[net::srv:sock::discovery] start\n"; | ||
1318 | /* begin listening to incoming connections */ | ||
1319 | if(listen(m_socketfd, LISTEN_QUEUE_SIZE) < 0) | ||
1320 | { | ||
1321 | debug() << "[net::srv::sock::discovery] listen() failed: " << errno << "\n"; | ||
1322 | return; | ||
1323 | } | ||
1324 | |||
1325 | /* handle connections */ | ||
1326 | while(!m_discovery_exit) | ||
1327 | { | ||
1328 | /* since accept() is blocking, use select to ensure a timeout */ | ||
1329 | struct timeval tmo = m_timeout; /* NOTE select() can overwrite timeout */ | ||
1330 | fd_set set; | ||
1331 | FD_ZERO(&set); | ||
1332 | FD_SET(m_socketfd, &set); | ||
1333 | /* wait for some activity */ | ||
1334 | int ret = select(m_socketfd + 1, &set, nullptr, nullptr, &tmo); | ||
1335 | if(ret < 0 || !FD_ISSET(m_socketfd, &set)) | ||
1336 | continue; | ||
1337 | int clifd = accept(m_socketfd, nullptr, nullptr); | ||
1338 | if(clifd >= 0) | ||
1339 | { | ||
1340 | debug() << "[net::srv::sock::discovery] new client\n"; | ||
1341 | /* set timeout for the client operations */ | ||
1342 | setsockopt(clifd, SOL_SOCKET, SO_RCVTIMEO, (char *)&m_timeout, sizeof(m_timeout)); | ||
1343 | setsockopt(clifd, SOL_SOCKET, SO_SNDTIMEO, (char *)&m_timeout, sizeof(m_timeout)); | ||
1344 | client_arrived(to_srv_client(clifd)); | ||
1345 | } | ||
1346 | } | ||
1347 | debug() << "[net::srv:sock::discovery] stop\n"; | ||
1348 | } | ||
1349 | |||
1350 | } // namespace uri | ||
1351 | } // namespace net | ||