summaryrefslogtreecommitdiff
path: root/usbredirserver/usbredirserver.c
diff options
context:
space:
mode:
authorHans de Goede <hdegoede@redhat.com>2011-05-04 15:43:33 +0200
committerHans de Goede <hdegoede@redhat.com>2011-05-06 16:17:41 +0200
commit2c67fa9b3317dc5fbe109ad25c52efb0eea33858 (patch)
treed3bc87ed68b71e82e46a62f2d13c4162c11b147f /usbredirserver/usbredirserver.c
parentf993567b8df1edb3b2d50440d7cd8c5508d7981b (diff)
Put source files into subdirs
Diffstat (limited to 'usbredirserver/usbredirserver.c')
-rw-r--r--usbredirserver/usbredirserver.c348
1 files changed, 348 insertions, 0 deletions
diff --git a/usbredirserver/usbredirserver.c b/usbredirserver/usbredirserver.c
new file mode 100644
index 0000000..67a9bf0
--- /dev/null
+++ b/usbredirserver/usbredirserver.c
@@ -0,0 +1,348 @@
+/* usbredirserver.c simple usb network redirection tcp/ip server (host).
+
+ Copyright 2010-2011 Red Hat, Inc.
+
+ Red Hat Authors:
+ Hans de Goede <hdegoede@redhat.com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <errno.h>
+#include <poll.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include "usbredirhost.h"
+
+#ifndef VERSION
+#define VERSION "usbredirserver 0.0"
+#endif
+
+static int verbose = usbredirparser_info;
+static int client_fd, running = 1;
+static libusb_context *ctx;
+static struct usbredirhost *host;
+
+static const struct option longopts[] = {
+ { "port", required_argument, NULL, 'p' },
+ { "verbose", required_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 }
+};
+
+static void usbredirserver_log(void *priv, int level, const char *msg)
+{
+ if (level <= verbose)
+ fprintf(stderr, "%s\n", msg);
+}
+
+static int usbredirserver_read(void *priv, uint8_t *data, int count)
+{
+ int r = read(client_fd, data, count);
+ if (r < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ return -1;
+ }
+ if (r == 0) { /* Client disconnected */
+ close(client_fd);
+ client_fd = -1;
+ }
+ return r;
+}
+
+static int usbredirserver_write(void *priv, uint8_t *data, int count)
+{
+ int r = write(client_fd, data, count);
+ if (r < 0) {
+ if (errno == EAGAIN)
+ return 0;
+ if (errno == EPIPE) { /* Client disconnected */
+ close(client_fd);
+ client_fd = -1;
+ return 0;
+ }
+ return -1;
+ }
+ return r;
+}
+
+static void usage(int exit_code, char *argv0)
+{
+ fprintf(exit_code? stderr:stdout,
+ "Usage: %s [-p|--port <port>] [-v|--verbose <0-5>] <usbbus-usbaddr|vendorid:prodid>\n",
+ argv0);
+ exit(exit_code);
+}
+
+static void invalid_usb_device_id(char *usb_device_id, char *argv0)
+{
+ fprintf(stderr, "Invalid usb device identifier: %s\n", usb_device_id);
+ usage(1, argv0);
+}
+
+static void run_main_loop(void)
+{
+ const struct libusb_pollfd **pollfds = NULL;
+ fd_set readfds, writefds;
+ int i, n, nfds;
+ struct timeval timeout, *timeout_p;
+
+ while (running && client_fd != -1) {
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+
+ FD_SET(client_fd, &readfds);
+ if (usbredirhost_has_data_to_write(host)) {
+ FD_SET(client_fd, &writefds);
+ }
+ nfds = client_fd + 1;
+
+ free(pollfds);
+ pollfds = libusb_get_pollfds(ctx);
+ for (i = 0; pollfds && pollfds[i]; i++) {
+ if (pollfds[i]->events & POLLIN) {
+ FD_SET(pollfds[i]->fd, &readfds);
+ }
+ if (pollfds[i]->events & POLLOUT) {
+ FD_SET(pollfds[i]->fd, &writefds);
+ }
+ if (pollfds[i]->fd >= nfds)
+ nfds = pollfds[i]->fd + 1;
+ }
+
+ if (libusb_get_next_timeout(ctx, &timeout) == 1) {
+ timeout_p = &timeout;
+ } else {
+ timeout_p = NULL;
+ }
+ n = select(nfds, &readfds, &writefds, NULL, timeout_p);
+ if (n == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("select");
+ break;
+ }
+ memset(&timeout, 0, sizeof(timeout));
+ if (n == 0) {
+ libusb_handle_events_timeout(ctx, &timeout);
+ continue;
+ }
+
+ if (FD_ISSET(client_fd, &readfds)) {
+ if (usbredirhost_read_guest_data(host)) {
+ break;
+ }
+ }
+ if (FD_ISSET(client_fd, &writefds)) {
+ if (usbredirhost_write_guest_data(host)) {
+ break;
+ }
+ }
+
+ for (i = 0; pollfds && pollfds[i]; i++) {
+ if (FD_ISSET(pollfds[i]->fd, &readfds) ||
+ FD_ISSET(pollfds[i]->fd, &writefds)) {
+ libusb_handle_events_timeout(ctx, &timeout);
+ break;
+ }
+ }
+ }
+ if (client_fd != -1) { /* Broken out of the loop because of an error ? */
+ close(client_fd);
+ client_fd = -1;
+ }
+ free(pollfds);
+}
+
+static void quit_handler(int sig)
+{
+ running = 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int o, server_fd = -1;
+ char *endptr, *delim;
+ int port = 4000;
+ int usbbus = -1;
+ int usbaddr = -1;
+ int usbvendor = -1;
+ int usbproduct = -1;
+ struct addrinfo *r, *res, hints;
+ struct sigaction act;
+ char port_str[16];
+ libusb_device_handle *handle;
+
+ while ((o = getopt_long(argc, argv, "hp:", longopts, NULL)) != -1) {
+ switch (o) {
+ case 'p':
+ port = strtol(optarg, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "Inalid value for --port: '%s'\n", optarg);
+ usage(1, argv[0]);
+ }
+ break;
+ case 'v':
+ verbose = strtol(optarg, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "Inalid value for --verbose: '%s'\n", optarg);
+ usage(1, argv[0]);
+ }
+ break;
+ case '?':
+ case 'h':
+ usage(o == '?', argv[0]);
+ break;
+ }
+ }
+ if (optind == argc) {
+ fprintf(stderr, "Missing usb device identifier argument\n");
+ usage(1, argv[0]);
+ }
+ delim = strchr(argv[optind], '-');
+ if (delim && delim[1]) {
+ usbbus = strtol(argv[optind], &endptr, 10);
+ if (*endptr != '-') {
+ invalid_usb_device_id(argv[optind], argv[0]);
+ }
+ usbaddr = strtol(delim + 1, &endptr, 10);
+ if (*endptr != '\0') {
+ invalid_usb_device_id(argv[optind], argv[0]);
+ }
+ } else {
+ delim = strchr(argv[optind], ':');
+ if (!delim || !delim[1]) {
+ invalid_usb_device_id(argv[optind], argv[0]);
+ }
+ usbvendor = strtol(argv[optind], &endptr, 16);
+ if (*endptr != ':') {
+ invalid_usb_device_id(argv[optind], argv[0]);
+ }
+ usbproduct = strtol(delim + 1, &endptr, 16);
+ if (*endptr != '\0') {
+ invalid_usb_device_id(argv[optind], argv[0]);
+ }
+ }
+ optind++;
+ if (optind != argc) {
+ fprintf(stderr, "Excess non option arguments\n");
+ usage(1, argv[0]);
+ }
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = quit_handler;
+ sigaction(SIGINT, &act, NULL);
+ sigaction(SIGHUP, &act, NULL);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGQUIT, &act, NULL);
+
+ if (libusb_init(&ctx)) {
+ fprintf(stderr, "Could not init libusb\n");
+ exit(1);
+ }
+
+ libusb_set_debug(ctx, verbose);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG | AI_NUMERICSERV | AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+
+ sprintf(port_str, "%d", port);
+ if (getaddrinfo(NULL, port_str, &hints, &res) != 0) {
+ perror("getaddrinfo");
+ exit(1);
+ }
+
+ for (r = res; r != NULL; r = r->ai_next) {
+ server_fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (server_fd == -1)
+ continue;
+
+ if (bind(server_fd, r->ai_addr, r->ai_addrlen) == 0)
+ break;
+
+ close(server_fd);
+ }
+ freeaddrinfo(res);
+
+ if (r == NULL) {
+ fprintf(stderr, "Could not bind to port: %s\n", port_str);
+ exit(1);
+ }
+
+ if (listen(server_fd, 1)) {
+ perror("listen");
+ exit(1);
+ }
+
+ while (running) {
+ client_fd = accept(server_fd, NULL, 0);
+ if (client_fd == -1) {
+ if (errno == EINTR) {
+ continue;
+ }
+ perror("accept");
+ break;
+ }
+
+ fcntl(client_fd, F_SETFL,
+ (long)fcntl(client_fd, F_GETFL) | O_NONBLOCK);
+
+ /* Try to find the specified usb device */
+ if (usbvendor != -1) {
+ handle = libusb_open_device_with_vid_pid(ctx, usbvendor,
+ usbproduct);
+ if (!handle) {
+ fprintf(stderr,
+ "Could not open an usb-device with vid:pid %04x:%04x\n",
+ usbvendor, usbproduct);
+ exit(1);
+ }
+ } else {
+ /* FIXME */
+ fprintf(stderr, "bus-addr usb device indentification is not implemented\n");
+ exit(1);
+ }
+ if (!handle) {
+ close(client_fd);
+ continue;
+ }
+
+ host = usbredirhost_open(handle, usbredirserver_log,
+ usbredirserver_read, usbredirserver_write,
+ NULL, VERSION, verbose);
+ if (!host)
+ exit(1);
+ run_main_loop();
+ usbredirhost_close(host);
+ }
+
+ close(server_fd);
+ libusb_exit(ctx);
+ exit(0);
+}