summaryrefslogtreecommitdiff
path: root/utils/hwstub/lib/hwstub_net.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/hwstub/lib/hwstub_net.cpp')
-rw-r--r--utils/hwstub/lib/hwstub_net.cpp1351
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
30namespace hwstub {
31namespace net {
32
33/**
34 * Context
35 */
36context::context()
37 :m_state(state::HELLO), m_error(error::SUCCESS)
38{
39}
40
41context::~context()
42{
43}
44
45std::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
51std::string context::default_unix_path()
52{
53 return "hwstub";
54}
55
56std::string context::default_tcp_domain()
57{
58 return "localhost";
59}
60
61std::string context::default_tcp_port()
62{
63 return "6666";
64}
65
66namespace
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
173std::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
183std::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
192std::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
202uint32_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
207hwstub::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
212error 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
246void context::destroy_device_list(void *ptr)
247{
248 (void)ptr;
249}
250
251error 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
258bool 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
264uint32_t context::to_net_order(uint32_t u)
265{
266 return htonl(u);
267}
268
269uint32_t context::from_net_order(uint32_t u)
270{
271 return ntohl(u);
272}
273
274error 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
420void 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
451void 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 */
482socket_context::socket_context(int socket_fd)
483 :m_socketfd(socket_fd)
484{
485 set_timeout(std::chrono::milliseconds(1000));
486}
487
488socket_context::~socket_context()
489{
490 stop_context();
491 close(m_socketfd);
492}
493
494void 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
504error 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
527error 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 */
556device::device(std::shared_ptr<hwstub::context> ctx, uint32_t devid)
557 :hwstub::device(ctx), m_device_id(devid)
558{
559}
560
561device::~device()
562{
563}
564
565uint32_t device::device_id()
566{
567 return m_device_id;
568}
569
570error 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
593bool device::has_multiple_open() const
594{
595 return false;
596}
597
598/**
599 * Handle
600 */
601handle::handle(std::shared_ptr<hwstub::device> dev, uint32_t hid)
602 :hwstub::handle(dev), m_handle_id(hid)
603{
604}
605
606handle::~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
624error 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
648error 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
671error 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
694error 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
715error handle::exec_dev(uint32_t addr, uint16_t flags)
716{
717 (void) addr;
718 (void) flags;
719 return error::DUMMY;
720}
721
722error handle::status() const
723{
724 return hwstub::handle::status();
725}
726
727size_t handle::get_buffer_size()
728{
729 return 2048;
730}
731
732/**
733 * Server
734 */
735server::server(std::shared_ptr<hwstub::context> ctx)
736 :m_context(ctx)
737{
738 clear_debug();
739}
740
741server::~server()
742{
743}
744
745void 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
756std::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
766std::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
777std::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
783std::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
793void server::set_debug(std::ostream& os)
794{
795 m_debug = &os;
796}
797
798std::ostream& server::debug()
799{
800 return *m_debug;
801}
802
803server::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
809void server::client_thread2(server *s, client_state *cs)
810{
811 s->client_thread(cs);
812}
813
814uint32_t server::to_net_order(uint32_t u)
815{
816 return htonl(u);
817}
818
819uint32_t server::from_net_order(uint32_t u)
820{
821 return ntohl(u);
822}
823
824void 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
922void 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
936void 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
956error 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 */
1213socket_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
1221socket_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
1231std::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
1238void 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
1244int socket_server::from_srv_client(srv_client_t cli)
1245{
1246 return (int)(intptr_t)cli;
1247}
1248
1249socket_server::srv_client_t socket_server::to_srv_client(int fd)
1250{
1251 return (srv_client_t)(intptr_t)fd;
1252}
1253
1254void socket_server::discovery_thread1(socket_server *s)
1255{
1256 s->discovery_thread();
1257}
1258
1259void 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
1266error 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
1289error 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
1315void 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