пятница, 23 ноября 2012 г.

Подключение сканеров,принтеров и др. при помощи usbip в thinstation 5

Одним из главных недостатков использования тонких клиентов является порой невозможность использования периферийных устройств, таких принтеры, сканеры, МФУ, веб-камеры и т.д., подключенных напрямую к ТК. Но, как оказалось, не всё так мрачно и можно попытаться использовать эти устройства(но не тешьте себя иллюзиями о 100% гарантии их работы), напрямую подключив к ТК. В этом нам на помощь приходит пакет usbip. Эта технология имеет клиент-серверную структуру. Серверная и клиентская часть доступны только для линукса, под Windows доступна только клиентская часть, но нам это только и требуется, так как thinstation является ОС из семейства Linux (да простят меня гики ;). Способ, описанный здесь, не совсем тривиальный, так что наберитесь терпения, возможно оно того стоит для вас.


Итак, начнём с самого главного. Серверная часть этого пакета лежит в ядре в виде модулей. Скажу больше, вам не нужно перекомпилировать ядро в thinstation, потому как они уже включены по-умолчанию. Это два модуля usbip-core.ko и usbip-host.ko. На самом деле есть ещё один vhci-hcd.ko, но он вам может понадобится только если ваш клиент всё тот же линукс(имеется ввиду связка линукс-линукс). Для того, чтобы эти модули были собраны в ваш образ(я имею ввиду использование параметров machine из вашего build.conf, а не ситуацию "./buld --allmodules") вам необходимо добавить эти модули в ваш(и) файл(ы) module.list вручную, потому что эти модули вспомогательные и не учтутся скриптом hwlister.sh:

module usbip-core
module usbip-host

Теперь нам необходима клиентская часть(за исключением модуля vhci-hcd.ko), несмотря на то, что наш ТК выступает в роли сервера. Клиентская часть находится всё в том же ядре, только в userspace части. Для этого нам понадобятся исходники нашего ядра. Итак, скачиваем их:

z1kk0@work-pc:~> cd thinstation
z1kk0@work-pc:~> su
Пароль:
root@work-pc:/home/z1kk0/thinstation # ./setup-chroot
[root@TS_chroot]/# cd ts/ports/components/kernel-TS
[root@TS_chroot]/ts/ports/components/kernel-TS# pkgmk -d -kw

Теперь нам необходимо скомпилировать клиентскую часть вручную - она не собирается вместе с остальным ядром, но для начала мы проведём несколько манипуляций. Это связано с тем, что разработчик(и) немного "перемудрил(и)" в новых версиях ядра и клиентская часть под Windows отказывается работать с серверной частью, выдавая вот такие ошибки в командной строке Windows:

usbip err: usbip_network.c: 121 (usbip_recv_op_common) recv op_common, -1
usbip err: usbip.c: 216 (query_exported_devices) recv op_common
usbip err: usbip.c: 288 (show_exported_devices) query

Более подробно вы можете ознакомиться здесь. Там же представлено и решение этой проблемы, которую мы и исправляем нашими манипуляциями. Итак, перейдём в каталог с исходниками клиентской части(обратите внимание на версию ядра, она скорее всего уже будет иная):

root@work-pc:/home/z1kk0/thinstation # ./setup-chroot
[root@TS_chroot]/# cd /ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace/

Находим файл configure.ac и меняем в строке указания версии значение 0x00000111 на 0x00000106

AC_DEFINE([USBIP_VERSION], [0x00000106], [binary-coded decimal version number])

Теперь перейдём в каталог src

[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace# cd src

и добавим в шапку файла usbip_network.h следующие директивы

#define USBIP_CMD_SUBMIT 0x0001
#define USBIP_CMD_UNLINK 0x0002
#define USBIP_RET_SUBMIT 0x0003
#define USBIP_RET_UNLINK 0x0004

#define USBIP_DIR_OUT 0x00
#define USBIP_DIR_IN 0x01

Для добавления всех строк я пользовался редактором vi.
Все манипуляции проведены, теперь приступим к сборке пакета. Выйдем на ступень вверх из каталога src и выполним все необходимые процедуры:

[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace/src# cd ..
[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace# ./autogen.sh
[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace# ./configure --exec-prefix=/ts/5.1/packages/usbip
[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace# make install

Параметром exec-prefix я указал установщику положить библиотеки и бинарники в каталог, где лежат все пакеты, которые могут быть добавлены через параметр package файла build.conf. Перейдём в этот каталог и посмотрим содержимое:

[root@TS_chroot]/ts/ports/components/kernel-TS/work/src/linux-3.4.5/drivers/staging/usbip/userspace# cd /ts/5.1/packages/usbip
[root@TS_chroot]/ts/5.1/packages/usbip# ls -l
total 8
drwxr-xr-x 2 root root 4096 Nov 22 09:51 sbin
drwxr-xr-x 2 root root 4096 Nov 22 09:51 lib

Переименуем каталог sbin в bin(точнее переместим):

[root@TS_chroot]/ts/5.1/packages/usbip# mv sbin bin

Создадим в корне нашего пакета файл dependencies и добавим туда слово base, чтобы наш пакет зависил от базовых утилит(они нам понадобятся в дальнейшем):

[root@TS_chroot]/ts/5.1/packages/usbip# cat > dependencies
base
CTRL+D
CTRL+D - это сочетание клавиш, которое необходимо нажать после набора слова base.

И добавим в файл build.conf параметр:

package usbip

Теперь нам осталось совсем немного до того момента, как мы сможем закинуть наш пакет в образ и заставить его работать.
Перед нами стоит задача довести процедуру связывания устройств с сервисом usbip до автоматизма, иными словами, при подключении нового устройства или смена порта подключения наше устройство автоматически "мапилось" через usbip, без выковыривания нужных параметров usbip и дополнительной пересборки образа. В этом нам поможет udev - менеджер устройств. У нас возникает только один нюанс - средствами udev невозможно различить устройства по типам - принтеры, сканеры, накопители, hid-устройства и т.д., а это важно, потому что нам не нужно "прокидывать" мышку на сервер через usbip, тем более что устройство становится полностью недоступным для ТК после подключения к нему клиентом usbip. Выход в данной ситуации может быть следующим - перечислить в правилах производителя устройства(т.н. вендор) и, соответственно, таким образом различать устройства. Ну что ж, воплотим слова в жизнь.
В каталоге /ts/5.1/packages/base/lib/udev/rules.d находятся все правила, мы создадим дополнительно своё 99-usbip.rules(всё должно быть в одной строке!):

ACTION=="add", ENV{ID_BUS}=="usb", ENV{ID_VENDOR}=="Samsung", RUN+="usbip_bind %E{ID_VENDOR_ID} %E{ID_MODEL_ID}"

Немного расшифрую смысл написанного. Первые три параметра(ACTION и два ENV) действуют по принципу логического И, т.е. при добавлении устройства (ACTION=="add") usb (ENV{ID_BUS}=="usb") производителя Samsung (ENV{ID_VENDOR}=="Samsung") должна выполнятся команда (RUN+="usbip_bind %E{ID_VENDOR_ID} %E{ID_MODEL_ID}"). Таким образом, если вам нужны ещё устройства, например, Canon, Hewlett-Packard, Epson и др., то вам нужно в этом же файле создать точно такие строки, только в ENV{ID_VENDOR} указать нужного вам производителя. Следует заметить, что название производителя не может быть взято "с потолка", его можно посмотреть, например, выполнив команду "udevadm monitor --env | grep ID_VENDOR", подключившись к ТК по telnet или ssh, и подключив ваше устройство(после выполнения команды).
Как вы могли заметить, в параметре RUN присутствует usbip_bind. Это скрипт, который автоматически подвязывает наше устройство на usbip, при этом нам не нужно будет заботится об особых параметрах, которые передаются команде usbip - это всё за нас будет делать наш скрипт. Этот скрипт мы положем в /ts/5.1/packages/base/lib/udev, так как это корневая директория для udev. Итак, создадим скрипт, дадим права на выполнение и заполним его:

[root@TS_chroot]/ts/5.1/packages/usbip# cd /ts/5.1/packages/base/lib/udev
[root@TS_chroot]/ts/5.1/packages/base/lib/udev# touch usbip_bind
[root@TS_chroot]/ts/5.1/packages/base/lib/udev# chmod 0755 usbip_bind

#! /bin/sh

BUSID=`/bin/usbip list -l | /bin/grep $1:$2 | /bin/awk '{print $3}'`
USB_IP=`/bin/ps | /bin/grep -v grep | /bin/grep "usbipd -D"`

if [ -n "$USB_IP" ]; then
#монтируем наше устройство в нужный порт
/bin/usbip bind -b $BUSID
else
#подгружаем наши модули
modprobe usbip-core
modprobe usbip-host
#наличие файла в этом месте обязательно для usbipd(мы просто делаем ссылку)
mkdir /usr/share/hwdata
ln -s /lib/usb.ids /usr/share/hwdata/usb.ids
#запускаем серверную часть в режим демона
/bin/usbipd -D
#монтируем наше устройство в нужный порт
/bin/usbip bind -b $BUSID
fi

Рассмотрим этот скрипт подробнее. Параметр BUSID - это необходимый параметр команды usbip, его мы вычисляем автоматически с помощью параметров ID_VENDOR_ID и ID_MODEL_ID(они передаются параметрами в скрипт из нашего правила udev). Далее, как вы видите, идет условный оператор if. Всё дело в том, что если ваше устройство уже подключено на момент включения ТК, то при включении ТК вам нужно запустить службу usbipd, а если вы будете подключать устройство уже во время работы ТК, то вам нет необходимоти этого делать. Поэтому здесь мы и воспользовались условным оператором if, а условие "-n $USB_IP" определяет запущен ли процесс usbipd.

Теперь нам остаётся собрать образ, загрузится с ТК, подключится в консоль и убедится, что наше устройство готово к использованию со стороны сервера:

Z1kk0@work-pc:~> telnet 10.10.10.10
Trying 10.10.10.10...
Connected to 10.10.10.10.
Escape character is '^]'.

ts_test_1 login: root
Password:
ts_test_1:~# lsmod | grep usbip
usbip_host 12943 0
usbip_core 5618 1 usbip_host
usbcore 124913 4 usbip_host,usbhid,uhci_hcd,ehci_hcd
ts_test_1:~# usbip list -l
Local USB devices
=================
- busid 2-1 (046d:c018)
2-1:1.0 -> usbhid

- busid 2-2 (04a9:2220)
2-2:1.0 -> usbip-host

Как видите строка "2-2:1.0 -> usbip-host" показывает, что наше устройство уже готово со стороны сервера.

Теперь остался последний штрих в нашем нелегком деле - настроить клиента. Клиентом в данном случае будет Windows 2008 R2, на других версиях Windows возможно не заведётся, но если такая уж необходимость, то надо пробовать.
Итак, идём вот сюда и скачиваем вторую версию клиента. Распакуем это "добро" в каталог C:\ (желательно, чтобы название каталога было usbip, а не usbip_win..., это просто для дальнейшего удобства). Теперь добавим новое "старое устройство". Всё это описано в файле USAGE скачанного нами клиента. Итак, по шагам:

1. Открываем "Панель управления" - "Диспетчер устройств"
2. Правой кнопкой мыши на имени компьютера - "Установить старое устройство"
3. Откроется мастер, жмёте "Далее"
4. В следующем окне выбираете "Установка оборудования, выбранного из списка вручную" и жмёте "Далее"
5. Выбираете "Системные устройства" - "Далее"
6. "Установить с диска" указываете путь, куда распаковали клиента, и снова "Далее"
7. Выберите "USB/IP Enumerator", нажмите "ОК", "Далее" и "Готово"

Теперь необходимо подключить наше устройство, присоединенное к ТК. Открываем командную строку и пишем:

C:\Users\Administrator>cd c:\usbip
C:\usbip>c:\usbip>usbip -l 10.10.10.10
- 10.10.10.10
2-2: Canon, Inc. : CanoScan LIDE 25 (04a9:2220)
: /sys/devices/pci0000:00/0000:00:1d.1/usb2/2-2
: Vendor Specific Class / unknown subclass / unknown protocol (ff/00/ff)

: 0 - Vendor Specific Class / unknown subclass / unknown protocol (ff/00/ff)


c:\usbip>
C:\usbip>usbip -a 10.10.10.10 2-2
new usb device attached to usbvbus port 1

Receive sequence: 3900

Командой "usbip -l 10.10.10.10" мы вывели список всех устройств с нашего ТК, "расшаренных" через usbip(10.10.10.10 - ip нашего ТК).
Командой "usbip -a 10.10.10.10 2-2" мы примали это устройство к себе на наш терминальный сервер(2-2 это тот самый busid, необходимый для присоединения устройства).
Теперь в диспетчере устройств у вас есть новое неопознанное устройство. Ставите на него драйвера и оно должно заработать как ему и положено.
Коммандую строку закрывать нельзя, иначе связь обрывается. Есть проще решение - написать скрипт и выполнять в фоне. Самый простой вариант выглядит так:

On Error Resume Next
Set wshshell = CreateObject("wscript.shell")
WshShell.Run "cmd /c cd c:\usbip & usbip -a 10.10.10.10 2-2", 0, FALSE

Этот скрипт можно повесить в качестве logon-скрипта. Но есть один недостаток - если связь с терминалом потеряна и сессия остаётся незавершенной, то при входе вы снова попадаете в свою сессию, но скрипт уже не запустится, а связь с устройством прервана. Поэтому в таком случае есть два варианта - либо завершить сессию и войти снова, либо запустить скрипт вручную не перезапуская сессию. Конечно, можно написать скрипт как службу, которая будет постоянно мониторить доступные устройства по определенному адресу или диапазону адресов, но это уже нетривиальная задача для написания подобного решения. Простыми командами тут не обойдешься и скорее всего нужно использовать PowerShell. На данный момент у меня нет возможности написания такого скрипта, так что этот вопрос остаётся за вами. Буду рад увидеть от вас решения по автоматизации работы со стороны Windows.

23 комментария:

  1. Спасибо за статью!!!
    А ссылкой на получившийся образ можете поделиться ;) ?

    ОтветитьУдалить
    Ответы
    1. Советую вам перечитать статью. Я буквально с полчаса назад внёс коррективы, существенно улучшающие функциональность работы usbip. Что касается образа, то он врятли для вас подойдет, потому как будет ограничен в вариациях оборудования, на котором сможет работать. Это связано с новой философией thinstation. Вам лучше собрать его самому. Инструкция по сборке есть у меня в блоге http://z1kk0.blogspot.com/2012/06/thinstation-5.html . Если будут вопросы, задавайте, постараюсь ответить

      Удалить
    2. Но ведь пакеты можно подключать из конфигурационных файлов.
      Т.к. тот же скайп, SANE выкладывают в виде пакетов так почему нельзя usbip.

      И если пакр машин разношостный то такие пляски с бубном для каждой машины это же застрелиться.

      Удалить
    3. а что сложного один раз собрать в образ?

      Удалить
  2. В каталоге /ts/5.1/packages/base/lib/udev/rules.d мы создадим дополнительно своё 99-usbip.rules

    ACTION=="add", ENV{ID_BUS}=="usb", ENV{ID_VENDOR}=="Samsung", RUN+="usbip_bind %E{ID_VENDOR_ID} %E{ID_MODEL_ID}"

    это означает, что принтер Samsung подключится, только если его дернут по usb после старта ТК, ведь стартового в /etc/init.d скрипта запуска usbip нет.
    Запустится он с помощью usb_bind.
    Может usb_bind нужно еще и из init.d запускать, но как тогда узнать busid?

    ОтветитьУдалить
    Ответы
    1. не нужно ничего дёргать, всё работает при старте систему(я же написал детально о скрипте) и скрипт в /etc/init.d в данном случае не требуется

      Удалить
    2. Этот комментарий был удален автором.

      Удалить
    3. а с терминала он у вас видит расшаренные устройства?

      Удалить
  3. ваш скрипт usb_bind создает линк с /lib/usb.ids которого там нет (и после компиляции он там тоже не появляется). но я взял его из комплекта исходников c sourceforge - так что все работает.

    еще хотелось бы услышать комментарий на тему правки версии в configure.ac и директив в файл usbip_network.h - вы ведь не с потолка взяли эти данные?

    а вообще ГИГАРЕСПКТ за статью - я такую инструкцию искал ГОД! (http://permalink.gmane.org/gmane.network.thinstation.devel/10482)

    если надо могу поделится своими успехами по Thinstation - использую PXE и ISO (для флэшек) сборки для загрузки бездисковых станций Pegatron D525, подключаюсь через xfreerdp к win 2k3 и 2к8 (2k12 тоже пробовал, но там заблокировано управление терминальной сессией)

    ОтветитьУдалить
  4. касаемо usb.ids, то возможно у меня пакет взялся по зависимостям от других пакетов, как посоветовали ниже, вы можете создать файл dependencies в /ts/5.1/packages/usbip и добавить туда строку ids.

    касаемо configure.ac, то я указывал ссылку, откуда брал эти значения, вот она http://sourceforge.net/projects/usbip/forums/forum/418507/topic/4581128/index/page/1

    ОтветитьУдалить
  5. Мда... вы правы - третья страница, 6-е сообщение...
    Иголка в стоге сена. Ну, в любом случае, спасибо, что вы ее нашли.

    ОтветитьУдалить
  6. Этот комментарий был удален автором.

    ОтветитьУдалить
  7. У меня, пока не добавил скрипт в rc2.d c содержимым:

    #! /bin/sh
    mkdir /usr/share/hwdata
    ln -s /lib/usb.ids /usr/share/hwdata/usb.ids
    modprobe usbip-core
    modprobe usbip-host
    /bin/usbipd -D

    , usbip сам не стартовал.
    Теперь запускается, показывает устройства. Вот только клиент ни на Windows2008r2, ни на Windows7 не видит доступных устройств.
    Когда вручную расшариваешь устройство командой usbip bind -b 3-5 оно становится доступно клиентам. Но при попытке подключения ругается:

    usbip err: usbip_windows.c: 660 (import_device) no free port
    usbip err: usbip_windows.c: 840 (attach_device) query

    Кто-нибудь сталкивался с подобным?

    ОтветитьУдалить
    Ответы
    1. Запуск usbip.exe от имени локального администратора делали?

      Удалить
    2. Этот комментарий был удален автором.

      Удалить
  8. Проблема, судя по всему, была в незапущенном на тот момент usbipd на тонком клиенте. А не запускался он автоматом из-за того, что скрипты я прописывал не вручную, а просто скопировал из браузера (причем не с этой страницы, а с репоста на тинстейшн.про). На данный момент все работает.
    Для удобства, файл 99-usb.rules положил на tftp сервер и подгружаю его при старте тонкого клиента, чтобы не пересобирать каждый раз образ.
    Думаю попробовать изобразить виндовый клиент для автоматического подключения и корректного отключения устройств.
    Кстати, возник вопрос по безопасности всей этой схемы. По сути, подключиться к устройству может кто угодно в локальной сети, с любой машины (при наличии админских прав, конечно, но тем не менее). Может быть есть какие-нибудь варианты для предотвращения этого?

    ОтветитьУдалить
    Ответы
    1. по сути никакой безопасности нет. вот здесь https://blogs.williamhuang.org/?p=6 оч хорошо написано по этому поводу и не рекомендуется использовать в сетях WAN

      Удалить
  9. https://dl.dropboxusercontent.com/u/48985950/usbip-p.exe

    как все скорее всего знают, очень большой недостаток клиента usbip. он при обрыве связи виснет, и если у нас таких клиентов много, а вылетел только один, как определить что это нужный? фактически не реально ... это переписанный .ехе . с возможностью вешать наши девайсы на определенный порт. к примеру
    "> usbip-p.exe -а 10.10.1.1 2-2 1"
    и наш принтер "повесится" на порт 1 и при обрыве связи, мы просто и безболезненно можем его "деатачить" "> usbip.exe -d 1" .

    зы. в коде куча проверок .. и много закрытых и недоделанных функция . разбираться со всем желание нет .. по тому советую все команды использовать в родном .exe .А а модифицированным "атачить" на порт. Поясняю, если у вас есть "веселый велосипед" основанный на родном .exe то в этом он скорее всего работать перестанет.

    Пользуемся :) тестируем...

    ОтветитьУдалить
  10. Спасибо огромное! Все работает. У меня тоже usbip сам не стартанул, как у FeanaR Kabaniero. Взял usbip_bind отсюда и все запахало. Видимо на thinstation.pro намудрили.

    ОтветитьУдалить
  11. Да не намудрили, с кавычками косяк вышел...

    ОтветитьУдалить
  12. Начал делать по инструкции и наткнулся на то, что папочки usbip по адресу /ts/ports/kernel-modules/kernel-TS/work/src/linux-3.10.17/drivers/staging нет, не появляется.

    Есть только \ts\ports\kernel-modules\kernel-TS\work\src\linux-3.18.22\drivers\usb\usbip\

    [URL=http://fastpic.ru/view/73/2016/0316/c887ad88b3f87a37f1fc7a2461347f77.png.html][IMG]http://i73.fastpic.ru/thumb/2016/0316/77/c887ad88b3f87a37f1fc7a2461347f77.jpeg[/IMG][/URL]

    Где я накосячил? :)

    ОтветитьУдалить
  13. Зашёл на сайт, на который Вы ссылаетесь, у Вас надо изменить usbip_network.h, а в "оригинале" - usbip_common.h, почему?

    И переделал у себя статью, в свете изменений путей.
    http://it-advisor.ru/thin/93-usbip.html

    ОтветитьУдалить