summaryrefslogtreecommitdiffstats
path: root/drivers/staging/usbip/userspace/src/usbipd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/usbip/userspace/src/usbipd.c')
-rw-r--r--drivers/staging/usbip/userspace/src/usbipd.c570
1 files changed, 570 insertions, 0 deletions
diff --git a/drivers/staging/usbip/userspace/src/usbipd.c b/drivers/staging/usbip/userspace/src/usbipd.c
new file mode 100644
index 000000000000..ec9faac5ff8f
--- /dev/null
+++ b/drivers/staging/usbip/userspace/src/usbipd.c
@@ -0,0 +1,570 @@
+/*
+ *
+ * Copyright (C) 2005-2007 Takahiro Hirofuchi
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include <unistd.h>
+#include <netdb.h>
+#include <strings.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LIBWRAP
+#include <tcpd.h>
+#endif
+
+#define _GNU_SOURCE
+#include <getopt.h>
+#include <signal.h>
+
+#include "usbip.h"
+#include "usbip_network.h"
+
+#include <glib.h>
+
+static const char version[] = PACKAGE_STRING;
+
+
+static int send_reply_devlist(int sockfd)
+{
+ int ret;
+ struct usbip_exported_device *edev;
+ struct op_devlist_reply reply;
+
+
+ reply.ndev = 0;
+
+ /* how many devices are exported ? */
+ dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
+ reply.ndev += 1;
+ }
+
+ dbg("%d devices are exported", reply.ndev);
+
+ ret = usbip_send_op_common(sockfd, OP_REP_DEVLIST, ST_OK);
+ if (ret < 0) {
+ err("send op_common");
+ return ret;
+ }
+
+ PACK_OP_DEVLIST_REPLY(1, &reply);
+
+ ret = usbip_send(sockfd, (void *) &reply, sizeof(reply));
+ if (ret < 0) {
+ err("send op_devlist_reply");
+ return ret;
+ }
+
+ dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
+ struct usb_device pdu_udev;
+
+ dump_usb_device(&edev->udev);
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ pack_usb_device(1, &pdu_udev);
+
+ ret = usbip_send(sockfd, (void *) &pdu_udev, sizeof(pdu_udev));
+ if (ret < 0) {
+ err("send pdu_udev");
+ return ret;
+ }
+
+ for (int i=0; i < edev->udev.bNumInterfaces; i++) {
+ struct usb_interface pdu_uinf;
+
+ dump_usb_interface(&edev->uinf[i]);
+ memcpy(&pdu_uinf, &edev->uinf[i], sizeof(pdu_uinf));
+ pack_usb_interface(1, &pdu_uinf);
+
+ ret = usbip_send(sockfd, (void *) &pdu_uinf, sizeof(pdu_uinf));
+ if (ret < 0) {
+ err("send pdu_uinf");
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int recv_request_devlist(int sockfd)
+{
+ int ret;
+ struct op_devlist_request req;
+
+ bzero(&req, sizeof(req));
+
+ ret = usbip_recv(sockfd, (void *) &req, sizeof(req));
+ if (ret < 0) {
+ err("recv devlist request");
+ return -1;
+ }
+
+ ret = send_reply_devlist(sockfd);
+ if (ret < 0) {
+ err("send devlist reply");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int recv_request_import(int sockfd)
+{
+ int ret;
+ struct op_import_request req;
+ struct op_common reply;
+ struct usbip_exported_device *edev;
+ int found = 0;
+ int error = 0;
+
+ bzero(&req, sizeof(req));
+ bzero(&reply, sizeof(reply));
+
+ ret = usbip_recv(sockfd, (void *) &req, sizeof(req));
+ if (ret < 0) {
+ err("recv import request");
+ return -1;
+ }
+
+ PACK_OP_IMPORT_REQUEST(0, &req);
+
+ dlist_for_each_data(stub_driver->edev_list, edev, struct usbip_exported_device) {
+ if (!strncmp(req.busid, edev->udev.busid, SYSFS_BUS_ID_SIZE)) {
+ dbg("found requested device %s", req.busid);
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) {
+ /* should set TCP_NODELAY for usbip */
+ usbip_set_nodelay(sockfd);
+
+ /* export_device needs a TCP/IP socket descriptor */
+ ret = usbip_stub_export_device(edev, sockfd);
+ if (ret < 0)
+ error = 1;
+ } else {
+ info("not found requested device %s", req.busid);
+ error = 1;
+ }
+
+
+ ret = usbip_send_op_common(sockfd, OP_REP_IMPORT, (!error ? ST_OK : ST_NA));
+ if (ret < 0) {
+ err("send import reply");
+ return -1;
+ }
+
+ if (!error) {
+ struct usb_device pdu_udev;
+
+ memcpy(&pdu_udev, &edev->udev, sizeof(pdu_udev));
+ pack_usb_device(1, &pdu_udev);
+
+ ret = usbip_send(sockfd, (void *) &pdu_udev, sizeof(pdu_udev));
+ if (ret < 0) {
+ err("send devinfo");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+
+static int recv_pdu(int sockfd)
+{
+ int ret;
+ uint16_t code = OP_UNSPEC;
+
+
+ ret = usbip_recv_op_common(sockfd, &code);
+ if (ret < 0) {
+ err("recv op_common, %d", ret);
+ return ret;
+ }
+
+
+ ret = usbip_stub_refresh_device_list();
+ if (ret < 0)
+ return -1;
+
+ switch(code) {
+ case OP_REQ_DEVLIST:
+ ret = recv_request_devlist(sockfd);
+ break;
+
+ case OP_REQ_IMPORT:
+ ret = recv_request_import(sockfd);
+ break;
+
+ case OP_REQ_DEVINFO:
+ case OP_REQ_CRYPKEY:
+
+ default:
+ err("unknown op_code, %d", code);
+ ret = -1;
+ }
+
+
+ return ret;
+}
+
+
+
+
+static void log_addrinfo(struct addrinfo *ai)
+{
+ int ret;
+ char hbuf[NI_MAXHOST];
+ char sbuf[NI_MAXSERV];
+
+ ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, hbuf, sizeof(hbuf),
+ sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV);
+ if (ret)
+ err("getnameinfo, %s", gai_strerror(ret));
+
+ info("listen at [%s]:%s", hbuf, sbuf);
+}
+
+static struct addrinfo *my_getaddrinfo(char *host, int ai_family)
+{
+ int ret;
+ struct addrinfo hints, *ai_head;
+
+ bzero(&hints, sizeof(hints));
+
+ hints.ai_family = ai_family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+
+ ret = getaddrinfo(host, USBIP_PORT_STRING, &hints, &ai_head);
+ if (ret) {
+ err("%s: %s", USBIP_PORT_STRING, gai_strerror(ret));
+ return NULL;
+ }
+
+ return ai_head;
+}
+
+#define MAXSOCK 20
+static int listen_all_addrinfo(struct addrinfo *ai_head, int lsock[])
+{
+ struct addrinfo *ai;
+ int n = 0; /* number of sockets */
+
+ for (ai = ai_head; ai && n < MAXSOCK; ai = ai->ai_next) {
+ int ret;
+
+ lsock[n] = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
+ if (lsock[n] < 0)
+ continue;
+
+ usbip_set_reuseaddr(lsock[n]);
+ usbip_set_nodelay(lsock[n]);
+
+ if (lsock[n] >= FD_SETSIZE) {
+ close(lsock[n]);
+ lsock[n] = -1;
+ continue;
+ }
+
+ ret = bind(lsock[n], ai->ai_addr, ai->ai_addrlen);
+ if (ret < 0) {
+ close(lsock[n]);
+ lsock[n] = -1;
+ continue;
+ }
+
+ ret = listen(lsock[n], SOMAXCONN);
+ if (ret < 0) {
+ close(lsock[n]);
+ lsock[n] = -1;
+ continue;
+ }
+
+ log_addrinfo(ai);
+
+ /* next if succeed */
+ n++;
+ }
+
+ if (n == 0) {
+ err("no socket to listen to");
+ return -1;
+ }
+
+ dbg("listen %d address%s", n, (n==1)?"":"es");
+
+ return n;
+}
+
+#ifdef HAVE_LIBWRAP
+static int tcpd_auth(int csock)
+{
+ int ret;
+ struct request_info request;
+
+ request_init(&request, RQ_DAEMON, "usbipd", RQ_FILE, csock, 0);
+
+ fromhost(&request);
+
+ ret = hosts_access(&request);
+ if (!ret)
+ return -1;
+
+ return 0;
+}
+#endif
+
+static int my_accept(int lsock)
+{
+ int csock;
+ struct sockaddr_storage ss;
+ socklen_t len = sizeof(ss);
+ char host[NI_MAXHOST], port[NI_MAXSERV];
+ int ret;
+
+ bzero(&ss, sizeof(ss));
+
+ csock = accept(lsock, (struct sockaddr *) &ss, &len);
+ if (csock < 0) {
+ err("accept");
+ return -1;
+ }
+
+ ret = getnameinfo((struct sockaddr *) &ss, len,
+ host, sizeof(host), port, sizeof(port),
+ (NI_NUMERICHOST | NI_NUMERICSERV));
+ if (ret)
+ err("getnameinfo, %s", gai_strerror(ret));
+
+#ifdef HAVE_LIBWRAP
+ ret = tcpd_auth(csock);
+ if (ret < 0) {
+ info("deny access from %s", host);
+ close(csock);
+ return -1;
+ }
+#endif
+
+ info("connected from %s:%s", host, port);
+
+ return csock;
+}
+
+
+GMainLoop *main_loop;
+
+static void signal_handler(int i)
+{
+ dbg("signal catched, code %d", i);
+
+ if (main_loop)
+ g_main_loop_quit(main_loop);
+}
+
+static void set_signal(void)
+{
+ struct sigaction act;
+
+ bzero(&act, sizeof(act));
+ act.sa_handler = signal_handler;
+ sigemptyset(&act.sa_mask);
+ sigaction(SIGTERM, &act, NULL);
+ sigaction(SIGINT, &act, NULL);
+}
+
+
+gboolean process_comming_request(GIOChannel *gio, GIOCondition condition,
+ gpointer data __attribute__((unused)))
+{
+ int ret;
+
+ if (condition & (G_IO_ERR | G_IO_HUP | G_IO_NVAL))
+ g_error("unknown condition");
+
+
+ if (condition & G_IO_IN) {
+ int lsock;
+ int csock;
+
+ lsock = g_io_channel_unix_get_fd(gio);
+
+ csock = my_accept(lsock);
+ if (csock < 0)
+ return TRUE;
+
+ ret = recv_pdu(csock);
+ if (ret < 0)
+ err("process recieved pdu");
+
+ close(csock);
+ }
+
+ return TRUE;
+}
+
+
+static void do_standalone_mode(gboolean daemonize)
+{
+ int ret;
+ int lsock[MAXSOCK];
+ struct addrinfo *ai_head;
+ int n;
+
+
+
+ ret = usbip_names_init(USBIDS_FILE);
+ if (ret)
+ err("open usb.ids");
+
+ ret = usbip_stub_driver_open();
+ if (ret < 0)
+ g_error("driver open failed");
+
+ if (daemonize) {
+ if (daemon(0,0) < 0)
+ g_error("daemonizing failed: %s", g_strerror(errno));
+
+ usbip_use_syslog = 1;
+ }
+
+ set_signal();
+
+ ai_head = my_getaddrinfo(NULL, PF_UNSPEC);
+ if (!ai_head)
+ return;
+
+ n = listen_all_addrinfo(ai_head, lsock);
+ if (n <= 0)
+ g_error("no socket to listen to");
+
+ for (int i = 0; i < n; i++) {
+ GIOChannel *gio;
+
+ gio = g_io_channel_unix_new(lsock[i]);
+ g_io_add_watch(gio, (G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL),
+ process_comming_request, NULL);
+ }
+
+
+ info("usbipd start (%s)", version);
+
+
+ main_loop = g_main_loop_new(FALSE, FALSE);
+ g_main_loop_run(main_loop);
+
+ info("shutdown");
+
+ freeaddrinfo(ai_head);
+ usbip_names_free();
+ usbip_stub_driver_close();
+
+ return;
+}
+
+
+static const char help_message[] = "\
+Usage: usbipd [options] \n\
+ -D, --daemon \n\
+ Run as a daemon process. \n\
+ \n\
+ -d, --debug \n\
+ Print debugging information. \n\
+ \n\
+ -v, --version \n\
+ Show version. \n\
+ \n\
+ -h, --help \n\
+ Print this help. \n";
+
+static void show_help(void)
+{
+ printf("%s", help_message);
+}
+
+static const struct option longopts[] = {
+ {"daemon", no_argument, NULL, 'D'},
+ {"debug", no_argument, NULL, 'd'},
+ {"version", no_argument, NULL, 'v'},
+ {"help", no_argument, NULL, 'h'},
+ {NULL, 0, NULL, 0}
+};
+
+int main(int argc, char *argv[])
+{
+ gboolean daemonize = FALSE;
+
+ enum {
+ cmd_standalone_mode = 1,
+ cmd_help,
+ cmd_version
+ } cmd = cmd_standalone_mode;
+
+
+ usbip_use_stderr = 1;
+ usbip_use_syslog = 0;
+
+ if (geteuid() != 0)
+ g_warning("running non-root?");
+
+ for (;;) {
+ int c;
+ int index = 0;
+
+ c = getopt_long(argc, argv, "vhdD", longopts, &index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'd':
+ usbip_use_debug = 1;
+ continue;
+ case 'v':
+ cmd = cmd_version;
+ break;
+ case 'h':
+ cmd = cmd_help;
+ break;
+ case 'D':
+ daemonize = TRUE;
+ break;
+ case '?':
+ show_help();
+ exit(EXIT_FAILURE);
+ default:
+ err("getopt");
+ }
+ }
+
+ switch (cmd) {
+ case cmd_standalone_mode:
+ do_standalone_mode(daemonize);
+ break;
+ case cmd_version:
+ printf("%s\n", version);
+ break;
+ case cmd_help:
+ show_help();
+ break;
+ default:
+ info("unknown cmd");
+ show_help();
+ }
+
+ return 0;
+}