[PATCH v14 07/10] usbip: exporting devices: new application-side daemon

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux