diff options
Diffstat (limited to 'drivers/staging/usbip/userspace/src/usbipd.c')
-rw-r--r-- | drivers/staging/usbip/userspace/src/usbipd.c | 570 |
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; +} |