This patch is the new application(vhci)-side daemon specific code. The daemons are consisting three files. usbip.c : common code. usbip_dev.c: device(stub)-side specific code. usbip_app.c: application(vhci)-side specific code - this patch. Here, device-side daemon is EXISTING-1 and application-side daemon is NEW-1 in figure below. libsrc/vhci_driver.c:read_record() is refactored to be reused for function vhci_find_device() which finds attached device from requested remote host. EXISTING) - invites devices from application(vhci)-side +------+ +------------------+ device--+ STUB | | application/VHCI | +------+ +------------------+ (server) (client) 1) # usbipd ... start daemon = = = 2) # usbip list --local 3) # usbip bind <--- list bound devices --- 4) # usbip list --remote <--- import a device ------ 5) # usbip attach = = = X disconnected 6) # usbip detach 7) usbip unbind NEW) - dedicates devices from device(stub)-side +------+ +------------------+ device--+ STUB | | application/VHCI | +------+ +------------------+ (client) (server) 1) # usbipa ... start daemon = = = 2) # usbip list --local 3) # usbip connect --- export a device ------> = = = 4) # usbip disconnect --- un-export a device ---> Bind and unbind are done in connect and disconnect internally. Signed-off-by: Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx> --- tools/usb/usbip/libsrc/vhci_driver.c | 105 +++++++++----- tools/usb/usbip/libsrc/vhci_driver.h | 1 + tools/usb/usbip/src/Makefile.am | 7 +- tools/usb/usbip/src/usbipd.c | 29 ++-- tools/usb/usbip/src/usbipd.h | 2 +- tools/usb/usbip/src/usbipd_app.c | 200 +++++++++++++++++++++++++++ tools/usb/usbip/src/usbipd_dev.c | 10 +- 7 files changed, 307 insertions(+), 47 deletions(-) diff --git a/tools/usb/usbip/libsrc/vhci_driver.c b/tools/usb/usbip/libsrc/vhci_driver.c index 5843f43..8b94ab5 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.c +++ b/tools/usb/usbip/libsrc/vhci_driver.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2005-2007 Takahiro Hirofuchi + * Copyright (C) 2015-2016 Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx> */ #include "usbip_common.h" @@ -154,67 +155,78 @@ static int get_nports(void) return nports; } -/* - * Read the given port's record. - * - * To avoid buffer overflow we will read the entire line and - * validate each part's size. The initial buffer is padded by 4 to - * accommodate the 2 spaces, 1 newline and an additional character - * which is needed to properly validate the 3rd part without it being - * truncated to an acceptable length. - */ -static int read_record(int rhport, char *host, unsigned long host_len, - char *port, unsigned long port_len, char *busid) +static int __read_record(int rhport, char *buffer, size_t buffer_len) { - int part; FILE *file; char path[PATH_MAX+1]; - char *buffer, *start, *end; - char delim[] = {' ', ' ', '\n'}; - int max_len[] = {(int)host_len, (int)port_len, SYSFS_BUS_ID_SIZE}; - size_t buffer_len = host_len + port_len + SYSFS_BUS_ID_SIZE + 4; - - buffer = malloc(buffer_len); - if (!buffer) - return -1; snprintf(path, PATH_MAX, VHCI_STATE_PATH"/port%d", rhport); file = fopen(path, "r"); if (!file) { err("fopen"); - free(buffer); return -1; } - if (fgets(buffer, buffer_len, file) == NULL) { err("fgets"); - free(buffer); fclose(file); return -1; } fclose(file); + return 0; +} + +/* + * Read the given port's record. + * + * To avoid buffer overflow we will read the entire line and + * validate each part's size. The initial buffer is padded by 4 to + * accommodate the 2 spaces, 1 newline and an additional character + * which is needed to properly validate the 3rd part without it being + * truncated to an acceptable length. + */ +static int read_record(int rhport, char *host, int host_len, + char *port, int port_len, char *busid, int busid_len) +{ + int part; + char *buffer, *start, *end; + char delim[] = {' ', ' ', '\n'}; + char * const str[] = {host, port, busid}; + int max_len[] = {host_len, port_len, busid_len}; + int str_len; + size_t buffer_len = host_len + port_len + busid_len + 4; + + buffer = malloc(buffer_len); + if (!buffer) + goto err_out; + + if (__read_record(rhport, buffer, buffer_len)) + goto err_free_buffer; + /* validate the length of each of the 3 parts */ start = buffer; for (part = 0; part < 3; part++) { end = strchr(start, delim[part]); - if (end == NULL || (end - start) > max_len[part]) { - free(buffer); - return -1; + if (end == NULL) + goto err_free_buffer; + str_len = (end - start); + if (str[part]) { + if (str_len >= max_len[part]) + goto err_free_buffer; + memcpy(str[part], start, str_len); + *(str[part] + str_len) = 0; } start = end + 1; } - - if (sscanf(buffer, "%s %s %s\n", host, port, busid) != 3) { - err("sscanf"); - free(buffer); - return -1; - } - free(buffer); return 0; + +err_free_buffer: + free(buffer); +err_out: + return -1; } #define OPEN_HC_MODE_FIRST 0 @@ -313,6 +325,31 @@ int usbip_vhci_get_free_port(void) return -1; } +struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid) +{ + struct usbip_imported_device *idev; + int ret; + char rhost[NI_MAXHOST]; + char rbusid[SYSFS_BUS_ID_SIZE]; + + for (int i = 0; i < vhci_driver->nports; i++) { + idev = &vhci_driver->idev[i]; + + if (idev->status == VDEV_ST_NULL || + idev->status == VDEV_ST_NOTASSIGNED) + continue; + + ret = read_record(idev->port, rhost, sizeof(rhost), NULL, 0, + rbusid, sizeof(rbusid)); + if (!ret && + !strncmp(host, rhost, sizeof(rhost)) && + !strncmp(busid, rbusid, sizeof(rbusid))) { + return vhci_driver->idev + i; + } + } + return NULL; +} + int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, uint32_t speed) { char buff[200]; /* what size should be ? */ @@ -395,7 +432,7 @@ int usbip_vhci_imported_device_dump(struct usbip_imported_device *idev) return 0; ret = read_record(idev->port, host, sizeof(host), serv, sizeof(serv), - remote_busid); + remote_busid, sizeof(remote_busid)); if (ret) { err("read_record"); read_record_error = 1; diff --git a/tools/usb/usbip/libsrc/vhci_driver.h b/tools/usb/usbip/libsrc/vhci_driver.h index c85988c..a812851 100644 --- a/tools/usb/usbip/libsrc/vhci_driver.h +++ b/tools/usb/usbip/libsrc/vhci_driver.h @@ -45,6 +45,7 @@ int usbip_vhci_refresh_device_list(void); int usbip_vhci_get_free_port(void); +struct usbip_imported_device *usbip_vhci_find_device(char *host, char *busid); int usbip_vhci_attach_device2(uint8_t port, int sockfd, uint32_t devid, uint32_t speed); diff --git a/tools/usb/usbip/src/Makefile.am b/tools/usb/usbip/src/Makefile.am index 1aa5156..8fdebce 100644 --- a/tools/usb/usbip/src/Makefile.am +++ b/tools/usb/usbip/src/Makefile.am @@ -2,11 +2,16 @@ AM_CPPFLAGS = -I$(top_srcdir)/libsrc -DUSBIDS_FILE='"@USBIDS_DIR@/usb.ids"' AM_CFLAGS = @EXTRA_CFLAGS@ LDADD = $(top_builddir)/libsrc/libusbip.la -sbin_PROGRAMS := usbip usbipd +sbin_PROGRAMS := usbip usbipd usbipa usbip_SOURCES := usbip.h utils.h usbip.c utils.c usbip_network.c \ usbip_attach.c usbip_detach.c usbip_list.c \ usbip_bind.c usbip_unbind.c usbip_port.c \ usbip_connect.c usbip_disconnect.c +usbip_CFLAGS := $(AM_CFLAGS) usbipd_SOURCES := usbip_network.h usbipd.c usbipd_dev.c usbip_network.c +usbipd_CFLAGS := $(AM_CFLAGS) + +usbipa_SOURCES := usbip_network.h usbipd.c usbipd_app.c usbip_network.c +usbipa_CFLAGS := $(AM_CFLAGS) -DUSBIP_DAEMON_APP diff --git a/tools/usb/usbip/src/usbipd.c b/tools/usb/usbip/src/usbipd.c index 14fdb40..66c66de 100644 --- a/tools/usb/usbip/src/usbipd.c +++ b/tools/usb/usbip/src/usbipd.c @@ -63,11 +63,13 @@ static const char usbipd_help_string[] = "\n" " -6, --ipv6\n" " Bind to IPv6. Default is both.\n" +#ifndef USBIP_DAEMON_APP "\n" " -e, --device\n" " Run in device mode.\n" " Rather than drive an attached device, create\n" " a virtual UDC to bind gadgets to.\n" +#endif "\n" " -D, --daemon\n" " Run as a daemon process.\n" @@ -93,7 +95,7 @@ static void usbipd_help(void) printf(usbipd_help_string, usbip_progname, usbip_default_pid_file); } -static int usbipd_recv_pdu(int connfd) +static int usbipd_recv_pdu(int connfd, char *host, char *port) { uint16_t code = OP_UNSPEC; int ret; @@ -115,7 +117,7 @@ static int usbipd_recv_pdu(int connfd) for (op = usbipd_recv_pdu_ops; op->code != OP_UNSPEC; op++) { if (op->code == code) { if (op->proc) - ret = (*(op->proc))(connfd); + ret = (*(op->proc))(connfd, host, port); else { err("received an unsupported opcode: %#0x", code); @@ -153,12 +155,12 @@ static int tcpd_auth(int connfd) } #endif -static int do_accept(int listenfd) +static int do_accept(int listenfd, char *host, int host_len, + char *port, int port_len) { int connfd; struct sockaddr_storage ss; socklen_t len = sizeof(ss); - char host[NI_MAXHOST], port[NI_MAXSERV]; int rc; memset(&ss, 0, sizeof(ss)); @@ -169,8 +171,8 @@ static int do_accept(int listenfd) return -1; } - rc = getnameinfo((struct sockaddr *)&ss, len, host, sizeof(host), - port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); + rc = getnameinfo((struct sockaddr *)&ss, len, host, host_len, + port, port_len, NI_NUMERICHOST | NI_NUMERICSERV); if (rc) err("getnameinfo: %s", gai_strerror(rc)); @@ -194,14 +196,15 @@ int process_request(int listenfd) { pid_t childpid; int connfd; + char host[NI_MAXHOST], port[NI_MAXSERV]; - connfd = do_accept(listenfd); + connfd = do_accept(listenfd, host, NI_MAXHOST, port, NI_MAXSERV); if (connfd < 0) return -1; childpid = fork(); if (childpid == 0) { close(listenfd); - usbipd_recv_pdu(connfd); + usbipd_recv_pdu(connfd, host, port); exit(0); } close(connfd); @@ -443,7 +446,9 @@ int main(int argc, char *argv[]) { "ipv6", no_argument, NULL, '6' }, { "daemon", no_argument, NULL, 'D' }, { "debug", no_argument, NULL, 'd' }, +#ifndef USBIP_DAEMON_APP { "device", no_argument, NULL, 'e' }, +#endif { "pid", optional_argument, NULL, 'P' }, { "tcp-port", required_argument, NULL, 't' }, { "help", no_argument, NULL, 'h' }, @@ -472,7 +477,11 @@ int main(int argc, char *argv[]) cmd = cmd_standalone_mode; usbipd_driver_init(); for (;;) { - opt = getopt_long(argc, argv, "46DdeP::t:hv", longopts, NULL); + opt = getopt_long(argc, argv, "46Dd" +#ifndef USBIP_DAEMON_APP + "e" +#endif + "P::t:hv", longopts, NULL); if (opt == -1) break; @@ -502,9 +511,11 @@ int main(int argc, char *argv[]) case 'v': cmd = cmd_version; break; +#ifndef USBIP_DAEMON_APP case 'e': usbipd_driver_set(USBIPD_DRIVER_TYPE_DEVICE); break; +#endif case '?': usbipd_help(); default: diff --git a/tools/usb/usbip/src/usbipd.h b/tools/usb/usbip/src/usbipd.h index 9b69717..f15b131 100644 --- a/tools/usb/usbip/src/usbipd.h +++ b/tools/usb/usbip/src/usbipd.h @@ -31,7 +31,7 @@ extern char *usbip_default_pid_file; struct usbipd_recv_pdu_op { uint16_t code; - int (*proc)(int connfd); + int (*proc)(int connfd, char *host, char *port); }; extern struct usbipd_recv_pdu_op usbipd_recv_pdu_ops[]; diff --git a/tools/usb/usbip/src/usbipd_app.c b/tools/usb/usbip/src/usbipd_app.c new file mode 100644 index 0000000..ce7438e --- /dev/null +++ b/tools/usb/usbip/src/usbipd_app.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2011 matt mooney <mfm@xxxxxxxxxxxxx> + * 2005-2007 Takahiro Hirofuchi + * Copyright (C) 2015-2016 Nobuo Iwata <nobuo.iwata@xxxxxxxxxxxxxxx> + * + * 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 program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifdef HAVE_CONFIG_H +#include "../config.h" +#endif + +#define _GNU_SOURCE +#include <errno.h> +#include <unistd.h> +#include <netdb.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> + +#include "vhci_driver.h" +#include "usbip_common.h" +#include "usbip_network.h" +#include "usbipd.h" + +char *usbip_progname = "usbipa"; +char *usbip_default_pid_file = "/var/run/usbipa"; + +int driver_open(void) +{ + if (usbip_vhci_driver_open()) { + err("please load " USBIP_CORE_MOD_NAME ".ko and " + USBIP_VHCI_DRV_NAME ".ko!"); + return -1; + } + return 0; +} + +void driver_close(void) +{ + usbip_vhci_driver_close(); +} + +static int refresh_device_list(void) +{ + return usbip_vhci_refresh_device_list(); +} + +struct usbipd_driver_ops usbipd_driver_ops = { + .open = driver_open, + .close = driver_close, + .refresh_device_list = refresh_device_list, +}; + +static int import_device(int sockfd, struct usbip_usb_device *udev, + char *host, char *port, char *busid, + uint32_t *status) +{ + int rc; + int rhport; + + dbg("Sockfd:%d", sockfd); + dump_usb_device(udev); + + do { + rhport = usbip_vhci_get_free_port(); + if (rhport < 0) { + err("no free port"); + *status = ST_NO_FREE_PORT; + goto err_out; + } + + rc = usbip_vhci_attach_device(rhport, sockfd, udev->busnum, + udev->devnum, udev->speed); + if (rc < 0 && errno != EBUSY) { + err("import device"); + *status = ST_NA; + goto err_out; + } + } while (rc < 0); + + rc = usbip_vhci_create_record(host, port, busid, rhport); + if (rc < 0) { + err("record connection"); + *status = ST_NA; + goto err_detach_device; + } + + return 0; + +err_detach_device: + usbip_vhci_detach_device(rhport); +err_out: + return -1; +} + +static int recv_request_export(int sockfd, char *host, char *port) +{ + struct op_export_request req; + uint32_t status = ST_OK; + int rc; + + memset(&req, 0, sizeof(req)); + + rc = usbip_net_recv(sockfd, &req, sizeof(req)); + if (rc < 0) { + dbg("usbip_net_recv failed: export request"); + return -1; + } + PACK_OP_EXPORT_REQUEST(0, &req); + + rc = import_device(sockfd, &req.udev, host, port, req.udev.busid, + &status); + if (rc < 0) + dbg("export request busid %s: failed", req.udev.busid); + + rc = usbip_net_send_op_common(sockfd, OP_REP_EXPORT, status); + if (rc < 0) { + dbg("usbip_net_send_op_common failed: %#0x", OP_REP_EXPORT); + return -1; + } + + dbg("export request busid %s: complete %u", req.udev.busid, status); + + return 0; +} + +static int unimport_device(char *host, struct usbip_usb_device *udev, + uint32_t *status) +{ + int rc; + struct usbip_imported_device *idev; + + idev = usbip_vhci_find_device(host, udev->busid); + if (idev == NULL) { + err("no imported port %s %s", host, udev->busid); + *status = ST_DEVICE_NOT_FOUND; + return -1; + } + + rc = usbip_vhci_detach_device(idev->port); + if (rc < 0) { + err("no imported port %d %s %s", idev->port, host, udev->busid); + *status = ST_NA; + return -1; + } + + usbip_vhci_delete_record(idev->port); + + return 0; +} + +static int recv_request_unexport(int sockfd, char *host, char *port) +{ + struct op_unexport_request req; + uint32_t status = ST_OK; + int rc; + + (void)port; + + memset(&req, 0, sizeof(req)); + + rc = usbip_net_recv(sockfd, &req, sizeof(req)); + if (rc < 0) { + dbg("usbip_net_recv failed: unexport request"); + return -1; + } + PACK_OP_UNEXPORT_REQUEST(0, &req); + + rc = unimport_device(host, &req.udev, &status); + if (rc < 0) + dbg("unexport request busid %s: failed", req.udev.busid); + + rc = usbip_net_send_op_common(sockfd, OP_REP_UNEXPORT, status); + if (rc < 0) { + dbg("usbip_net_send_op_common failed: %#0x", OP_REP_UNEXPORT); + return -1; + } + + dbg("unexport request busid %s: complete %u", req.udev.busid, status); + + return 0; +} + +struct usbipd_recv_pdu_op usbipd_recv_pdu_ops[] = { + {OP_REQ_EXPORT, recv_request_export}, + {OP_REQ_UNEXPORT, recv_request_unexport}, + {OP_UNSPEC, NULL} +}; diff --git a/tools/usb/usbip/src/usbipd_dev.c b/tools/usb/usbip/src/usbipd_dev.c index 0d59edd..62e8d62 100644 --- a/tools/usb/usbip/src/usbipd_dev.c +++ b/tools/usb/usbip/src/usbipd_dev.c @@ -90,7 +90,7 @@ struct usbipd_driver_ops usbipd_driver_ops = { .refresh_device_list = refresh_device_list, }; -static int recv_request_import(int sockfd) +static int recv_request_import(int sockfd, char *host, char *port) { struct op_import_request req; struct usbip_exported_device *edev; @@ -100,6 +100,9 @@ static int recv_request_import(int sockfd) int error = 0; int rc; + (void)host; + (void)port; + memset(&req, 0, sizeof(req)); rc = usbip_net_recv(sockfd, &req, sizeof(req)); @@ -208,10 +211,13 @@ static int send_reply_devlist(int connfd) return 0; } -static int recv_request_devlist(int connfd) +static int recv_request_devlist(int connfd, char *host, char *port) { int rc; + (void)host; + (void)port; + rc = send_reply_devlist(connfd); if (rc < 0) { dbg("send_reply_devlist failed"); -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html